Can you pass by reference while using the ternary operator? - php

Simple question, simple code. This works:
$x = &$_SESSION['foo'];
This does not:
$x = (isset($_SESSION['foo']))?&$_SESSION['foo']:false;
It throws PHP Parse error: syntax error, unexpected '&'. Is it just not possible to pass by reference while using the conditional operator, and why not? Also happens if there's a space between the ? and &.

In the very simply case, this expression, which is illegal;
$c = condition ? &$a : &$b; // Syntax error
can be written like this:
$c = &${ condition ? 'a' : 'b' };
In your specific case, since you're not assigning by reference if the condition is false, a better option seems to be:
$x = isset($_SESSION['foo']) ? $x = &$_SESSION['foo'] : false;

Simple answer: no. You'll have to take the long way around with if/else. It would also be rare and possibly confusing to have a reference one time, and a value the next. I would find this more intuitive, but then again I don't know your code of course:
if(!isset($_SESSION['foo'])) $_SESSION['foo'] = false;
$x = &$_SESSION['foo'];
As to why: no idea, probably it has to with at which point the parser considers something to be an copy of value or creation of a reference, which in this way cannot be determined at the point of parsing.

Let's try:
$x =& true?$y:$x;
Parse error: syntax error, unexpected '?', expecting T_PAAMAYIM_NEKUDOTAYIM in...
$x = true?&$y:&$x;
Parse error: syntax error, unexpected '&' in...
So, you see, it doesn't even parse. Wikken is probably right as to why it's not allowed.
You can get around this with a function:
function &ternaryRef($cond, &$iftrue, &$iffalse=NULL) {
if ($cond)
return $iftrue;
else
return $iffalse;
}
$x = 4;
$a = &ternaryRef(true, $x);
xdebug_debug_zval('a');
$b = &ternaryRef(false, $x);
xdebug_debug_zval('b');
gives:
a: (refcount=2, is_ref=1),int 4
b: (refcount=1, is_ref=0),null

Unfortunately, you can't.
$x=false;
if (isset($_SESSION['foo']))
$x=&$_SESSION['foo'];

The commentary on this bug report might shed some light on the issue:
http://bugs.php.net/bug.php?id=53117.
In essence, the two problems with trying to assign a reference from the result of a ternary operator are:
Expressions can't yield references, and
$x = (expression) is not a reference assignment, even if (expression) is a reference (which it isn't; see point 1).

Related

Reference assignment with a ternary operator

I've commented the code for easier readibility.
function logRequest($cached = false) {
// This function logs the number of two types of events: (1) cached and (2) uncached/upstream request
// References to frequently addressed array keys, for brevity
$lc = &$_SESSION['loads']['cache'];
$lu = &$_SESSION['loads']['upstream'];
// Add to one session variable or the other, depending on the type of event
$s = ($cached ? &$lc : &$lu);
$s = (isset($s) ? $s : 0) + 1;
// Begin dumping the counts to a file, but only every 25 calls for each client
if(($lc ?? 0) + ($lu ?? 0) < 25) return;
$logArray = (file_exists($logFile = '../runtime/requests.json') ? json_decode(file_get_contents($logFile),true) : []);
// Define array structure for dumping to a file
$today = (new DateTime())->format('Y-m-d');
$ac = &$logArray[$today]['cache access'];
$au = &$logArray[$today]['upstream request'];
// Do incrementing
$ac = (isset($ac) ? $ac : 0) + $lc;
$au = (isset($au) ? $au : 0) + $lu;
// Reset counters in the session
$lc = $lu = 0;
// Finally, save to file
file_put_contents($logFile, json_encode($logArray, JSON_PRETTY_PRINT));
}
The line I am talking about is this:
$s = ($cached ? &$lc : &$lu);
And the error is:
Parse error: syntax error, unexpected '&' in...
How do I go about assigning a reference in this case? Surely there must be a way to use the ternary operator, right?
P.S. I am a very casual programmer so I would be grateful if you could point out any other bad practices in my code.
try this
$cached ? $s = &$lc : $s = &$lu;
Add some explanation about the code. Sorry for my bad english, I am ashamed to express in English.
If the value of the first subexpression is TRUE (non-zero), then the second subexpression is evaluated, and that is the result of the conditional expression. Otherwise, the third subexpression is evaluated, and that is the value.
this is from php expressions manual.
the reason of we can't use $s = $cached ? &$lc : &$lu; is ternary operator need a expression but &$lc and &$lu not a expression.
we can test this code
<?php
$a = 5;
$a;
It's no error.
but this
<?php
$a = 5;
&$a;
throw an error.
Parse error: syntax error, unexpected '&', expecting end of file in /usercode/file.php on line 3
The above is my opinion, I hope you can understand.
It's too difficult to using english for me.(愒o愒)
A working alternative would be employing dynamic variable names:
$s = &${$cached ? 'lc': 'lu'};
Further reading: https://stackoverflow.com/a/35772533/9986646

difference between $a==5 and 5==$a in php

Today I have faced a question, I was unable to answer it,
I have tried by making a php program but was unable to find out the exact reason for it
if $a=5 then both($a==5 and 5==$a) are giving me output as boolean true and,
if $a != 5 then both ($a==5 and 5==$a ) are giving me output as boolean false
Can anyone tell me what is the difference between $a==5 and 5==$a from any language point of view.
**Program**
$a = 3;
var_dump( 5==$a );
var_dump( $a==5 );
$a = 5;
var_dump( 5==$a );
var_dump( $a==5 );
**Output**
boolean false
boolean false
boolean true
boolean true
Comparisons like that are not affected by which value you write first. However, it is best practice to put the literal first, e.g. 5 == $x because if you mess up and only enter one equals sign, you'll get an error instead of an accidental value assignment, which is far easier to debug.
No difference
but 5 == $a prevents some error if you forgot one '='.
For example $a = 1
if you write if ($a = 5) - $a value becomes 5
if you write if (5 = $a) - you got error
It's just a technique, that prevents you from accidentally using assignment instead of comparison. The if operator happily accepts $a = 5, while 5 = $a throws an error, preventing you from creating a nasty bug.
5 == $a is logically the same as $a == 5
This format, commonly referred to as yoda conditions and does not affect the logical comparison.
https://en.wikipedia.org/wiki/Yoda_conditions
It is preferred, commonly in PHP, to prevent accidental assignment in conditions, which will always evaluate as either truthy or falsy, but will not actually perform the indented condition check:
if ($a = 5) {
// always run... oops
} else {
// never run
}
The WordPress PHP Coding Standards have a good explanation of this as well

Ternary operator when casting a variable

While writing is_numeric($var) ? (Int)$var : (String)$var;, I was wondering if it could be possible to move the ternary operator to the part where I cast the variable:
echo (is_numeric($var) ? Int : String)$var;
Not to my surprise, it didn't work:
PHP Parse error: syntax error, unexpected '$var' (T_VARIABLE)
Is this at all possible? Or maybe something close to what I'm trying to do? It's more of a curiosity thing than a need to use it.
Yes it's possible. This should work for you:
var_dump((is_numeric($var)?(int)$var :(string)$var));
As an example to test it you can easy do this:
$var = 5;
var_dump((true?(int)$var :(string)$var)); //Or var_dump((false?(int)$var :(string)$var));
Output:
int(5) //string(1) "5"
EDIT:
They only way i could think of to do something similar to what you want would be this:
settype($var, (is_numeric($var)?"int":"string"));
var_dump($var);
No; this is not possible. The ternary operator expects an expression which the casting operator is not.
It would however be possible to use first-class functions, which are expressions, with the ternary operator like so:
$toInt = function($var) {
return (int) $var;
};
$toString = function($var) {
return (string) $var;
};
$foo = "10";
var_dump(call_user_func(is_numeric($foo) ? $toInt : $toString, $foo));

Assign if variable is set

In PHP I find myself writing code like this frequently:
$a = isset($the->very->long->variable[$index])
? $the->very->long->variable[$index]
: null;
Is there a simpler way to do this? Preferably one that doesn't require me to write $the->very->long->variable[$index] twice.
An update, because PHP 7 is now out and is a game-changer on this point ; the previous answers are about PHP 5.
PHP 7 solves this issue. Because you are true at saying that it is frequent to write this in PHP, and that's absolutely not elegant.
In PHP 7 comes the Null Coalesce Operator (RFC), which is a perfect shorthand for the isset ternary condition.
Its goal is to replace this type of condition:
$var = isset($dict['optional']) ? $dict['optional'] : 'fallback';
By that:
$var = $dict['optional'] ?? 'fallback';
Even better, the null coalesce operators are chainable:
$x = null;
# $y = null; (undefined)
$z = 'fallback';
# PHP 7
echo $x ?? $y ?? $z #=> "fallback"
# PHP 5
echo isset($x) ? $x : (isset($y) ? $y : $z)
The null coalesce operator acts exactly like isset() : the subject variable's value is taken if:
The variable is defined (it exists)
The variable is not null
Just a note for PHP beginners: if you use the ternary condition but you know that the subject variable is necessarily defined (but you want a fallback for falsy values), there's the Elvis operator:
$var = $dict['optional'] ?: 'fallback';
With the Elvis operator, if $dict['optional'] is an invalid offset or $dict is undefined, you'll get a E_NOTICE warning (PHP 5 & 7). That's why, in PHP 5, people are using the hideous isset a ? a : b form when they're not sure about the input.
Sadly no, because the RFC has been declined. And because isset is not a function but a language construct you cannot write your own function for this case.
Note: Because this is a language construct and not a function, it cannot be called using variable functions.
If you only assign null instead of the non set variable, you can use:
$a = #$the->very->long->variable[$index];
# makes that instruction throw no errors
Assuming you know that $the->very->long->variable is set, and you're just worried about the array index....
$x = $the->very->long->variable;
$a = isset($x[$index]) ? $x[$index] : null;
Or for a more generic variant that you can use around you code:
function array_valifset($arr,$k, $default=null) {
return isset($arr[$k]) ? $arr[$k] : $default;
}
then call it like this for any array value:
$a = array_valifset($the->very->long->variable,$index);
I stumbled across the same problem and discovered that referencing an array element does not issue a notice or warning but returns null (at least PHP 5.6).
$foo = ['bar' => 1];
var_dump($bar = &$foo['bar']); // int 1
var_dump($baz = &$foo['baz']); // null
Inside an if statement:
if($bar = &$foo['bar']) {
echo $bar;
}
if($baz = &$foo['baz']) {
echo $baz;
}

which is slower? error suppressing or a dedicated function?

Take this:
$data = array('one'=>'1','three'=>'3');
Now which is better?
This:
echo #$data['two'];
or this:
function val($data,$key,$default){
if(isset($data[$key])){
return $data[$key];
}
return $default;
}
echo val($data,'two','');
or this:
echo isset($data['two'])?$data['two']:'';
or something else?
avoiding the notice: Notice: Undefined index: two in document on line #num
which one is the most efficient, and which one should I use?
I am wondering that maybe the super-slow error suppressing might be faster than having a dedicated function?
p.s. Lots of answers seem to assume that I am doing this as a form of optimization, this is not true, I am asking the "efficiency" part out of curiosity and the "which should I use" part because I need to use something and I want to know what I should default to.
p.p.s. most efficient and which used will most likely be different
Use whichever you like best. The slowness of your application does not come from this place.
This is common sense answer
"#" symbol will suppress PHP-generated error messages. suppress, notice will occure and error handling function will be called.
isset is part of the language construct, therefore it is much faster.
Use Ternary Operators isset($dat['index']) ? $data['index'] : null, because it looks clean and does not trigger error handling
Coming from java, I would suggest you to use the third option. This way you don't hide a code that doesn't work but you instead provide a default value when there is none.
The first way just hide an error, and the second is just way too long.
Error suppressing with # is know to be really slow. I've read that turning error reporting, doing something and then turning the old reporting level is still faster than just using #.
Regarding other two options - they are equal for me, but just for output, I would use 3rd variant with isset - simply looks nicer and no need to define extra function... If you are using a lot of output - then maybe function would reduce code repeat and would be more useful...
Php has this array_key_exists function that I think is the most correct way to handle what you're doing. As for speed, here is a test program I just made:
//testing #
$t = microtime(TRUE);
$a = array('one' => 1, 'three' => 3);
for ($i = 0; $i < 1000000; $i++)
$b = #$a['two'];
echo (microtime(TRUE) - $t)."\n";
//testing array_key_exists
$t = microtime(TRUE);
$a = array('one' => 1, 'three' => 3);
for ($i = 0; $i < 1000000; $i++)
$b = array_key_exists('two', $a) ? $a['two'] : '';
echo (microtime(TRUE) - $t)."\n";
//testing isset
$t = microtime(TRUE);
$a = array('one' => 1, 'three' => 3);
for ($i = 0; $i < 1000000; $i++)
$b = isset($a['two']) ? $a['two'] : '';
echo (microtime(TRUE) - $t)."\n";
and the results are:
5.9005348682404
9.6285729408264
0.32760310173035
So yeah, isset is noticeably faster.
Time has passed, reflections on the solution
Q. Which should I use by default?
A. Ternary operator.
Q. Which is fastest?
A. Ternary operator.
Q. Which is the most convenient?
A. Custom function.
# error surpressing actually works like this
it switches off error reporting completely for the entire php thread
it does the operation
it switches error reporting back on
which is why it is not recommended
sometimes I use this:
function issetor(&$variable, $or = NULL) {
return $variable === NULL ? $or : $variable;
}
because it is shorter to write:
echo issetor($data['two'],'');
than:
echo isset($data['two'])?$data['two']:'';
Warning: issetor($arr[$k],null); will set the $arr[$k] = null; if $arr[$k] is not already set
Other times I use custom array handling functions, depending on the situation.

Categories