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.
Related
I cant seem to find any discussion about this.
In JavaScript to check if something exists, and use a default if it doesn't is like this:
var myvariable = mysetting || 3
In PHP from what I understand, the same thing can be done like this:
$myvariable = $mysetting ?: 3;
Am i completely off on this? I understand the implications of not using isset() and not_empty() and all that, but if I just want to know if the variable exists and is truthy otherwise use a default - this should work I think. Does this syntax have any hidden bad things about it?
Because it doesn't work. That code will still throw a notice Notice: Undefined variable: mysetting in C:\wamp\www\x.php on line, which might be visible to the user, depending on the PHP settings. Apart from that, it will work. If notices are suppressed, then the end result is correct.
So, to get around that, you can either use isset, which isn't really a function, but a language construct, and is specifically designed to do this check:
$myvariable = isset($mysetting)? $mysetting: 3;
Or you can suppress the notice using the # operator:
$myvariable = #$mysetting ?: 3;
For this specific case, maybe it's acceptable, but in general the use of # is frowned upon by many. Personally, I would rather use a couple more characters and make it feel less 'dirty', but it's a matter of opinion.
Another reason why people may not use it, is that it's relatively new (PHP 5.3). Not everyone might know of this new syntax or be comfortable with it. They have been used to isset for years, and old habits die hard.
Those statements are not equivalent. From what I have found the javascript is equivalent to:
if (!mysetting) {
myvariable = 3;
} else {
myvariable = mysetting;
}
Whereas the equivalent PHP statement using the ternary operator would be:
$mysetting = isset($myvariable) ? $myvariable : 3;
aka:
if( isset($myvariable) ) {
$mysetting = $myvariable;
} else {
$mysetting = 3;
}
The ternary operator is essentially a shorthand for and if/else statement, and the first operand is interpreted as a logical expression.
I've been thinking of using reference assignment as a shortcut for dealing with potentially undefined variables.
In other words, instead of:
$foo = isset($this->blah['something']['else']) ? $this->blah['something']['else'] : null;
if (!is_null($foo) && ...){
//do something with $foo
}
I could do this:
$foo = &$this->blah['something']['else'];
if (!is_null($foo) && ...){
//do something with $foo
}
Seems simpler, right? Because of the way PHP handles assignment by reference, I don't have to worry about $this->blah['something']['else'] being defined, because if it doesn't exist it will be created automatically and set to NULL.
Is this strategy frowned upon?
If you just want to test if a variable is set and not null, then you better test:
if (! empty($this->blah['something']['else'])) {...}
This way you avoid creating two references to a value that could not even exist.
In my opinion, every time you create a new reference to the same value, your code becomes harder to understand at a glance.
If you need zero as non-empty, then you better create a global function like this:
function eempty(& $var) {
return empty($var) && $var !== 0;
}
I personally think it could be more conventional, but assigning a referred value like that seems a bit redundant to me. I'd just stick with the more commonly used ternary operator in your situation. Often it's a lot clearer to understand. Of course, you should follow your personal semantics as close as possible.
I started to learn OO a few days back, I'm quite OK with procedural coding but obviously that is not enough and I want to become a well versed coder with a lot of experience and knowledge, so first thing to learn completely must be OO followed by the right design patterns I think.
Anyhow, I have one thing where I'm stuck which I don't quite follow...
Static variables... I understand that a static variable doesn't lose it's value even if the containing function is finished executing, and will keep it's value if the same function is executed again, and again etc etc.
But what I don't understand is what exactly can you now assign to a static variable? The manual and also countless question on stackoverflow state you can not assign an expression to a static variable.
So I read the PHP manual, to find out exactly what is considered an expression? The manuals answer is (and I quote):
"In PHP, almost anything you write is an expression. The simplest yet most accurate way to define an expression is "anything that has a value"."
"When you type "$a = 5", you're assigning '5' into $a. '5', obviously, has the value 5, or in other words '5' is an expression"
http://php.net/manual/en/language.expressions.php
Now when you read about variable scope in the manual, they have exactly this example:
function test()
{
static $a = 0;
echo $a;
$a++;
}
So, the manual about variable scopes says static $a = 0; is fine, while the manual about expressions says $a = 5, would be an expression. Which is basically the same thing, just a 0 instead of a 5...
So I'm a bit confused now.
What exactly is an expression now and what exactly can I or can I not assign to static variables? :)
You cannot initialize a static variable using a non-constant expression. You can assign anything you like to it after it is initialized.
The difference is that static variables are initialized during the parsing phase, i.e. while PHP reads through the source code to figure out what's what. At that stage no code is executed, PHP just reads what you want it to do. Therefore, it will not execute code to initialize the variable.
static $foo = 'bar';
'bar' is a constant value which PHP can easily assign to the variable at parse time.
static $foo = Bar::baz();
Bar::baz() is an expression that needs to run, PHP needs to locate the class, load it if necessary, run the baz() method, which may do all sorts of different stuff... The same for 5 + 3, md5('bar') or anything that requires actual computation. PHP is simply not going to do all this dynamic stuff at parse time. Therefore you cannot initialize a static variable with anything but constant values.
At runtime, you can assign anything you like to a static variable. An often used pattern is this:
static $foo = null;
if ($foo === null) {
$foo = new SomeObject;
}
This keeps the instance of SomeObject in the static variable, but you cannot initialize the variable with it.
static keyword changes nothing. 0 is still an expression.
You can assign to static only constant values, that have determined value on parsing stage and have no any calculations.
So
static $a = 0;
is valid code;
static $b = $a + 1;
static $c = 1 + 5;
are not.
You have mixed up apples and oranges here. (Or/and the manual did a poor job explaining them.)
Static variables, as in your question, are not the same as static properies of a class.
See: http://php.net/manual/en/language.oop5.static.php
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.
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.