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.
Related
Firstly, let's say for the sake of argument, the reasons why I want to do this is arbitrary and not solution specific.
I would like to explicitly cast a variable, regardless of input, and increment it after typecasting, not with the original variable. For example, not like this:
$num = "47 downvotes";
(int) ++$num;
I am looking for something similar to this psuedo-coded version, or a variation thereof:
$num = "value";
++((int) $num);
For PHP being loose, I was really hoping this to work, but I can't use the Pre-increment operator without creating another variable first.
$num = "value";
$cast = (int) $num;
echo ++$cast;
While testing, I found that PHP is loose enough for it to work by adding a digit however:
$num = "47 dogs";
echo ((int) $num) + 1;
I also understand my first example, isn't wrong, but again, for arbitrary reasons, I need to make sure it has been casted prior to incrementing/decrementing.
So the question is, why is PHP loose enough for the latter to compile?
If you could provide resources or links to any reputable reading material I would appreciate that as well.
With explicit typecasting you have to assign the result to a variable. In your examples, you are trying to increment variables when they are strings, which fails or doesn't produce the result you expect in combination with typecasting.
Look at your original example:
<?php
$num = "47 downvotes";
echo $num . PHP_EOL;
echo ++$num;
The result of incrementing a string isn't what you expect it to be:
47 downvotes
47 downvotet
So your original supposition is that PHP doesn't work when in fact it does.
$num = "47 downvotes";
echo (int) ++$num . PHP_EOL;
$num2 = "47";
echo (int) ++$num2;
Output:
47
48
The process of typecasting is inherently complicated, and has all sorts of behavior that can produce unexpected results, and just isn't the catchall dependable "fix your input" that will let you find the numeric portion of any string available to you in a single line of code, but that doesn't mean that PHP is flawed.
I have a really quick question for you:
I read data from an Excel sheet and want to transform it into an assoc array. But sometimes there are no values given in some cells. So if this occurs I want to set the value of the array to 0.
right now I do it like that with the ternary operator and I'm glad I discovered that today:
(isset($excel->sheet[0]['cells'][$row][$value]) ? $excel->sheet[0]['cells'][$row][$value] : 0)
Is there a whay to shorten the repitition in this case? It works but it ain't that pretty :(
Although this is not recommended, I would go the following way (PHP 5.3):
(#$excel->sheet[0]['cells'][$row][$value] ? : 0);
Error suppression operator is a mess, but in this case the only thing you suppress is a well-known notice about undefined variable.
Another option (as stated by Álvaro G. Vicario) could be a simple cast to int (as NULL casts to 0):
(int)#$excel->sheet[0]['cells'][$row][$value];
Another option is making a function to check the existence of such variable – maybe it's a little over-engineering, overkill or just too much –:
function iset($array, $output) {
$args = func_get_args();
$val = $array;
for ($i = 1; $i < count($args) - 1; $i++) {
if (!isset($val[func_get_arg($i)])) {
return func_get_arg(func_num_args() - 1);
}
$val = $val[func_get_arg($i)];
}
return $val;
}
Then use the function like this:
$var = iset($excel->sheet, 0, 'cells', $row, $value, "DEFAULT_VALUE");
Please consider code below
<?php
$a = '';
echo empty($a) ? '' : substr($a, 0, 1); // Prints: ''
echo substr($a, 0, 1); // Prints: ''
Which "echo" is better? In first one always $a will be checked and substr will run normally and in second one, substr checks $a internally and may trigger some notice errors.
The first one is better.
You always need to prevent errors / warnings / notices.
Although I wouldn't even use the one-liner just for readability.
The input string must be one character or longer. So it is better to check before.
Please check : http://php.net/manual/en/function.substr.php
The most important part is that you differ between input and output variables:
$input['a'] = '';
$output['a'] = empty($input['a']) ? '' : substr($input['a'], 0, 1);
echo $output['a'];
How you write the validation is your business, it should deal with all edge cases and should not trigger any errors/warnings.
Edit: If you're concerned to reduce the number of function calls, the following snippet does not make use of any functions, only one language constructs:
$a = empty($a[0])?'':$a[0];
echo $a;
I tried the following code in both facebook's phpsh and the standard crappy php -a abomination for a repl:
$a = NULL;
echo $a['foobar'];
To my regret (I wouldn't call it surprise or disappointment when it concerns PHP) I don't get any errors or warnings or exceptions or anything.
Smarter languages like Ruby and Python both complain when trying to dereference a key from eg None or nil. How is PHP interpreting this situation? Is the only cure inserting is_null checks everywhere? Should I blame Smarty for not doing it for me?
According to PHP source code (Zend/zend_execute.c), only strings, arrays and objects can trigger errors when accessing an offset/index. The rest is more-or-less ignored:
$a = true;
$b = false;
$c = null;
$d = 1;
$e = 1.234;
$f = '';
$g = array();
$h = new stdClass;
echo $a[0]; // NULL
echo $b[0]; // NULL
echo $c[0]; // NULL
echo $d[0]; // NULL
echo $e[0]; // NULL
echo $f[0]; // E_NOTICE: Uninitialized string offset
echo $g[0]; // E_NOTICE: Undefined offset
echo $h[0]; // E_FATAL: Cannot use object as array
None of $a,$b,$c,$d or $e actually spit an error. Most of the times in the code I just see return; or return 0;, which means NULL, instead of a returned zval* (pointer) or zend_error() call. Hence the results above.
Whatever the reason why it has been done like this, it doesn't really matter. You should always check a variable for existence and/or nullity in such cases. The safest ways (slightly different behaviours) are isset and empty:
isset($a['foo']);
!empty($a['foo']);
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).