I need to assign one of two variables to a third variable, using the value of the second variable if the first is (bool)false or undefined.
I usually do this using ternary notation like so:
$foobar = ($some_prefix_and_some_variable_name) ? $some_prefix_and_some_variable_name : $bar ;
but sometimes this is not so pretty if the $foo variable name is very long, as it needs to be repeated in this notation.
My question is now, is it just as good to use this notation:
$foobar = $some_prefix_and_some_variable_name OR $foobar = $bar;
and is this notation interchangeable with the ternary version?
In PHP 5.3 there is also the short ternary notation:
$foobar = $fooooooooooooooooooooooooo ?: $bar ;
Because $foobar = $foo OR $foobar = $bar; evaluates to this:
Assign foo to foobar.
* Is there a value there?
* If not, assign bar to foobar.
While the other evaluates to:
Is there a value at foo?
* If so assign foobar = foo
* else assign foobar = bar
In the first example, if !foo you are assigning twice, if foo it can be faster. In the second example, you are only setting the value once total. I'll wager though that the speed difference is negligible.
The bigger issue here is readability. If 5.3 short notation isn't available, then I would still use the ternary notation if only because other programmers expect it.
For that matter, you will save more time and money by using the traditional ternary syntax if only because when people see your code they won't be asking themselves, WTF?
Since PHP 5.3 there is a special operator for this:
$foobar = $fooooooooooooooooooooooooo ?: $bar;
From the documentation on the ternary operator:
Since PHP 5.3, it is possible to leave out the middle part of the ternary operator. Expression expr1 ?: expr3 returns expr1 if expr1 evaluates to TRUE, and expr3 otherwise.
in matter of speed, this:
$foobar = $fooooooooooooooooooooooooo OR $foobar = $bar;
it's faster
No.
I don't know for sure whether the two notations are interchangeable, which means that you should not use the second one! Whether or not they are actually the same, if it is not immediately clear what the code does (as in this case), you should change it, unless you are absolutely sure that no one else will ever have to update this code. If I saw that written, I wouldn't be sure whether it was supposed to be like this, or whether there was some typo, and the bug was simply never discovered (seen it too many times!).
Ideally, with very long variable names, you should do this:
if($fooooooooooooooooooooooooo){
$foobar =$fooooooooooooooooooooooooo;
} else {
$foobar = $bar;
}
Just to make it easy to read, unless speed is of the essence, in which case you should use the ternary operator.
Related
Unfortunately I haven't found any official resource on this.
Is it allowed to use the ternary operator like this, to shorten and if/else statement:
(isset($someVar) ? $this->setMyVar('something') : $this->setMyVar('something else'));
In the PHP documentation, the ternary operator is explained with this example:
$action = (empty($_POST['action'])) ? 'standard' : $_POST['action'];
This makes me believe that my use case might work, but it not really valid because a setter function does not return anything.
Yes, you can. From the PHP Doc:
[...] the ternary operator is an expression, and [...] it doesn't evaluate to a variable, but to the result of an expression.
That quoted, although the ternary is mostly used for assigning values, you can use it as you suggested because a function call is an expression so the appropriate function will be evaluated (and therefore executed).
If your setter function would return a value, it would simply not be assigned to anything and would therefore be lost. However, since you said your setter doesn't return anything, the whole ternary operator will evaluate to nothing and return nothing. That's fine.
If this is more readable than a regular if/else is a different story. When working in a team, I would suggest to rather stick to a regular if/else.
Or, how about going with this syntax, which would be more common?
$this->myVar = isset($someVar) ? 'something' : 'something else';
Or, using your setter:
$this->setMyVar(isset($someVar) ? 'something' : 'something else');
I often find myself needing to write code with the following logical pattern:
$foo = isset($bar) ? $bar : $baz;
I know about the ?: syntax:
$foo = $bar ?: $baz;
...which, on the surface, appears to be what I'm looking for; however, it throws an undefined notice index when $bar is not set. It also uses the same logic as empty(), meaning that "empty" values like FALSE, 0, "0", etc. don't pass. Hence, it's not really equivalent.
Is there a shorter way of writing that code without throwing a notice when $bar is not set?
Edit:
To make it a bit more clear why I'm looking for a shortcut syntax, here's a better example:
$name = isset($employee->getName())
? $employee->getName()
: '<unknown>';
In this case, $employee might be an object from a 3rd-party library, and it might be a valid scenario that its name might be NULL. I'd like to set variable $name to the returned name (if there is one), but some sensible default if there isn't.
If the method call is more complex than just a getter, then the example becomes even more verbose, since we have to cache the result:
$bar = $some->reallyExpensiveOperation();
$foo = isset($bar) ? $bar : $baz;
I would only use the short hand ternary syntax when you explicitly predefine your variables or use an object with a magic getter. This is a very basic example of where I would normally use short hand ternary syntax
class Foo {
public function __get($name) {
return isset($this->{$name}) ? $this->{$name} : '';
}
}
$foo = new Foo();
$bar = $foo->baz ?: 'default';
Using the error control operator you could technically shorten it to:
$foo = #$bar ?: $baz;
If $bar isn't set, the value evaluates to null, and you get no error notice since you suppressed the error. Some, however, might not approve of this code since its not nice to use # since it masks errors and makes debugging more difficult.
Without that, no I don't believe there is a shorter notation that just using isset.
Edit: As noted by #drrcknlsn, this won't work if the variables are false, or 0, or even null, so it would seem to me that there is no avoiding the calling of isset.
When $bar is not defined, there isn't really a "shorter" way to write the same code.
There are two, what I would consider "hacks" to do it, however, they also may impact other things:
Error throttling with #, such as $foo = #$bar ?: $baz; will
do exactly what you want and throttle the undefined-error that is
thrown when $bar is undefined. If it is defined, it will also work
as desired. The downside is, however, that # can reduce efficiency
in your code if it's used repeatedly.
Turning off notices with
error_reporting(E_ALL & ~E_NOTICE);. This will still display all
regular errors but just not the notices, which will effectively hide
the "variable is undefined" error. The downside to this is that you
will not see any other notices.
Now, my obligatory personal opinion, I would suggest to continue writing it out full-hand. The ternary operator is already shorthand, effectively reducing the following:
if (isset($bar)) {
$foo = $bar;
} else {
$foo = $baz;
}
into a much shorter
$foo = isset($bar) ? $bar : $baz;
and it really doesn't take much more effort to write the full ternary versus the shorter-ternary (unless your variable names are ridiculously long, of course). Also, there are going to be more than a few instances where compound ternary operations (i.e. - several ternaries in one) that will cause the extra-shorthand to be unusable; so the short-savings is not programmatically/morally satisfying (in my own opinion).
UPDATE: To support your edit where you're assigning $bar to the return-value of a function and then $foo based off of it - you can combine the statements into a single-line like:
$foo = (($bar = $some->reallyExpensiveOperation()) != null) ? $bar : $baz;
This is almost the same code-length as the two lines, but it can be shortened slightly from here as well. For instance, if null isn't the only value that you consider "not valid", but false also counts, you can drop the != null portion entirely and allow the condition to be treated as a simple boolean value:
$foo = ($bar = $some->reallExpensiveOperation()) ? $bar : $baz;
In this example, $bar is still accessible after the ternary operation (i.e. - it doesn't lose scope), so unless you need to do pre-processing on the variable there isn't a big downside to this method, other than readability.
I've perused the questions on ternary operators vs. if/else structures, and while I understand that under normal circumstances there is no performance loss/gain in using ternary operators over if/else structures, I've not seen any mention of this situation. Language specific to PHP (but any language agnostic details are welcome) does the interpreter reassign values in situations like this:
$foo = 'bar'
$foo = strlen($foo) > 3 ? substr($foo, 0, 3) : $foo;
Since this would evaluate to $foo = $foo; is this inefficient, or does the interpreter simply overlook/discard this evaluation?
On a side note, what about:
!defined('SECURE') ? exit : null;
I don't know if your first example is inefficient, but it sure is pointless. I still think an if statement is clearer:
$foo = 'bar';
if (strlen($foo) > 3)
$foo = substr($foo, 0, 3);
And while the following works, it makes no sense to place null at the end because a ternary operator is meant to be used to evaluate expressions/values, but here null does nothing other than to prevent a parse error:
!defined('SECURE') ? exit : null;
More commonly, you would see this, an example of boolean short-circuiting (or exit doesn't execute if SECURE is not defined, because the or conditional expression evaluates to true automatically once at least one condition is found to be true):
defined('SECURE') or exit;
The point I'm trying to make is this: don't use ternary conditional expressions just because you can.
In this cases, I use the form presented by BoltClock:
if (strlen($foo) > 3) {
$foo = substr($foo, 0, 3);
}
PHP does not implement something more simple to work in this cases, yet :/
The topic that using a ternary here is not optimal has already been covered above. I'm going to address your question about whether it will reassign the value:
This depends on what you call "reassigning". PHP does not optimize, so the $foo = $foo will be evaluated. On the other hand this will not cause PHP to copy the value of $foo to a new chunk of memory. Probably PHP will just increase the refcount on $foo and then immediately decrease it (though I'm not sure about the exact implementation details of self-assignment). So, even though PHP will execute the statement, it won't affect performance (unless you choose to write $foo = $foo seven million times in your code).
There is always short-circuiting, although as #BoltClock said, an if statement is probably more readable in my opinion, and opens the door to else if and else conditions as well.
strlen($foo) > 3 && $foo = substr($foo, 0, 3);
The latter statement will only be executed if the former evaluates to TRUE.
I've seen the following lines of code and I know what they do, but I don't know how the second line works (and hence how to apply it to another situation).
$user = User::model()->findByPk(123);
empty($user->profile) and $user->profile = new Profile();
The code tries to look up the User from the database, and if there isn't a profile, creates a new for use later on.
I have also seen code before that goes something like the following:
$variable1 = $variable2 = $variable3;
It did something a bit more complex than simple assigning three things to be the same, but I'm finding it impossible to search for this type of thing to find out any information about it, let alone find the original code that I came across. I think it originally had an 'and' in there somewhere. Does anyone know how to search for code that has more than one equals sign in it that wasn't just an if statement?
Sorry for the two questions in one (and vague at that) and the terrible title (I'll fix it up when I know what the names are, if it's anything like a tenary statement)).
Using Logical Operators to Skip Code:
As php evaluates a line with the AND operator, if the fist part is false, the second part is not evaluated since it would not change the result.
So in this case, if empty() returns true, then php evaluates the right side. If empty() returns false, no more evaluation is done and the profile is not effected.
The php manual logical operators page has some illustrations of this.
Multiple Assignment Operators: The assignment operator assigns the right expression to the variable on the left.
$variable1 = $variable2 = $variable3;
In this case $variable2 is set to the value of $variable3 then $variable1 is set to the value of $variable2. The php manual assignment operators page covers this.
empty($user->profile) and $user->profile = new Profile();
in and statement, first element is evaluated first; if it's true, then second statement is evaluated, simple assignment should always evaluate to true, I presume. if the first element was false, second element is not evaluated.
You can read more about operator precedence in php docs.
It's basically the same as
if (empty($user->profile))
$user->profile = new Profile();
Weird syntax indeed...
What you're seeing is used in many languages. Here is an article for using it in JavaScript... but it explains the concept well.
http://css.dzone.com/articles/guard-and-default-operators-ja
This is a fairly bizarre way to write this. With a PHP expression, if the first part evaluates false, the expression will stop rendering.
Like if I write:
if (x == 5 and y == 2)
that will test if x==5, then if it does, it will test if y==2. If x != 5, it will never test the y == 2. So the code above is using that fact to test whether $user->profile is empty. Then if it is, it runs the next part of the expression, which is the assignment of $user->profile = new Profile(); Basically the same as:
if (empty($user->profile))
$user->profile = new Profile();
or
empty($user->profile) ? $user->profile = new Profile();
As far as your second question, the operator = is just an assignment, so it means that the value of $variable1 will be set to the value of $variable2, which will be set to the value of $variable3.
PHP does some very nice things with expressions, which are really helpful to learn. Check out this link for more info:
http://us.php.net/manual/en/language.expressions.php
$variable1 = $variable2 = $variable3;
Assignment (via the equal sign) in PHP has right to left precedence. Every expression in PHP has a return value. So the return value of the expression $variable3 is assigned to $variable2. Then the reurn value of the expression $variable2 = $variable3 is assigned to $variable1.
I want to set a variable to a value, but only if a condition is true.
Instead of doing the following:
if($myarray["foo"]==$bar){
$variablename=$myarray["foo"];
}
This can end up being quite long if the variable names are long, or perhaps it involves arrays, when it's quite simple what I want to do — set a value if a condition is true.
I would like to use the conditional operator, something like this:
$variablename=($myarray["foo"]=="bar")? $myarray["foo"]......
But this fails because I don't want the variable to be set at all if the statement is false.
Basically, what I'm trying to do is make the first example shorter. Perhaps the conditional operator is not the way though...
Does anyone have any suggestions?
It doesn't get much shorter than:
if($condition) $var = $value;
IMO, the best way to make your code sample shorter is:
if($myarray["foo"] == $bar)
$variablename = $myarray["foo"];
FYI, the name of the operator you're asking about isn't "the ternary operator", it's the conditional operator.
Since you ask, a way you could actually use the conditional operator to do what you're asking is:
$myarray['foo'] == $bar ? $variablename = $myarray['foo'] : null;
but that's somewhat horrifically ugly and very unmaintainable.
You could do this, but I wouldn't as it is pretty unreadable and stupid:
$myarray["foo"] == $bar ? $variablename = $myarray["foo"] : 0;
or
$myarray["foo"] == $bar && $variablename = $myarray["foo"];
Your right, ternary is not the way to go. It's there to handle the if and else part of the statement.
Just stick with the regular if statement.
if($myarray["foo"]==$bar) $variablename=$myarray["foo"];
The "problem" you have isn't really a problem. Your example code is very clear and maintainable. I would really say leave it like it is.
You -could- remove the braces, but that will have an impact on maintainability.
Your other alternative is to create a set_if_true(mixed array, string key, boolean conditional) wrapper function. It hides what is really happening but depending on your specific implementation it is a good option. (For instance a configuration type object, or caching backend)
Put != instead of == and ?: instead of just ?..
$variablename = ($myarray["foo"] != "bar") ?: $myarray["foo"];
is the same as
if($myarray["foo"] != "bar"){} else { $variablename = $myarray["foo"]; }
It might not be the smartest solution but it works. I like this one more
if($myarray["foo"] != "bar") {$variablename = $myarray["foo"]};
Set the variable to itself in the false case:
$variablename=($myarray["foo"]=="bar")? $myarray["foo"] : $variablename
You can put the original expression in the else part of the ternary operation, but if you want to guarantee single evaluation of the expression then you'll have to use a temporary variable and an if statement.
Ternary isn't the way, even though it can be written so that ternary works.
The reason is this: you're trying to use it in a way it's not intended, which will make your code awkward for other developers to read.