CodeIgniter Forums

Full Version: Checking a variable for existance without isset(), empty(), or any other function
You're currently viewing a stripped down version of our content. View the full version with proper formatting.

El Forum

[eluser]BrianDHall[/eluser]
Just ran into this today, and wondering if there is anything 'wrong' with my solution.

Consider this:

Code:
if ($real_array[$potential_element])
{
// do something
}

The problem - if $potential_element is not a valid index of the array, it'll throw a PHP notice. I don't want to do that, so normally I'd wrap it in a call to isset() or !empty(), etc.

But is there anything wrong with just doing this:

Code:
if (@$real_array[$potential_element])
{
// do something
}

??? Just a simple @ to shut PHP up, explicitly stating that "I know this might not be initialized - I understand, and I don't care".

This seems such a simpler and more readable solution to wrapping up something so simple in an unnecessary function call.

So, is ok (it works without error on my machines), or is there something I'm missing?

El Forum

[eluser]Developer13[/eluser]
isset() exists for a reason.

El Forum

[eluser]BrianDHall[/eluser]
[quote author="Developer13" date="1258768021"]isset() exists for a reason.[/quote]

...yes...to see if a variable is set. But what if you don't care if it is set, you only want to know if it is true?

My reasoning is that isset() determines if a variable is set, empty() determines if something is set to a non-empty value (0 is not empty, but a zero-length character string is empty)...but what what if you want to check to see if something is set AND true - in the simplest way possible?

if($var) seems to me to be the simplest way to do this - the function of if() is to test for truth, so if is only 'truth' that matters why wrap it in another test? It is perfectly reasonable for PHP to raise a warning for uninitialized values (in fact this helped me fix 3 bugs when working on this), so therefore if(@$var) becomes the simplest way to a) test for truth and b) explicitly state your understanding as a programmer as to the pitfalls of uninitialized variables.

What I'm wondering is if the use of @ still generates an internal PHP notice that it simply discards, and if so does isset() do the same thing?

My primary concern is that performance implication of generating notices, errors, or warnings and then discarding them, because this will be performed inside a loop that may execute multiple thousands of times per page view in this particular application - and I don't know how to check to see if PHP is doing this.

El Forum

[eluser]John_Betong[/eluser]
 

Your question certtainly is food for thought

From my limited experience I endeavour to write code without any errors because
the application seems to be much more nippier if there are no errors.

Using isset(...) I think is a KLUDGE because one is relying on PHP to trot off and
search to see if the variable has been set is in the boolean, numeric, string, array, etc
list of variables. After eliminating all possible options isset(...) will return your query.
This must take processing time.

I would be tempted to ensure your $potential_element has a default value and
therefore eliminate the search. Is it possible set a default value and run tests
with and without the default variable?
 
 
 
edit: spelling.

El Forum

[eluser]Colin Williams[/eluser]
First, like John_Betong alluded to, sometimes you want to provide default values to avoid isset() calls. But other times you do want the isset() call because the mere existence of a variable, even if empty, might have ramifications elsewhere in the application.

Second, don't confuse the wise goal of writing the least amount of code necessary with the not-so-wise goal of using the least amount of characters of code possible.

El Forum

[eluser]Chad Fulton[/eluser]
The answer to your question is yes, the way PHP internals works is that the @ symbol calls the error_reporting function twice, once before your function runs to set it to 0, and once afterwards to restore it to the original value, so it's the worst possible choice.

Now, I wrote up the following benchmark test, with these results:

Isset: 0.023620843887329
Array Key Exists: 0.079711198806763
Neither: 0.11602878570557
Neither w/ @: 0.13448619842529

So, you'll notice that even with error reporting disabled, if(isset($tmp[$i]) && $tmp[$i]) is faster than simply if($tmp[$i]), assuming (as I did in the benchmarking) that half of the time your variable will be initialized and half of the time it won't. Depending on the true distribution, YMMV.

The Neither w/ @ takes the most time, although in this case not too much more than Neither, since I assumed you'd be running with errors off by default.

Code:
<?php

$num =100000;
$tmp = array();

for ($i=0;$i<($num/2);$i++) {
    $tmp[$i]=1;
}

ini_set('display_errors', 0);
error_reporting(0);

$t1 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(isset($tmp[$i]) && $tmp[$i]);
}
$t2 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(array_key_exists($i,$tmp) && $tmp[$i]);
}
$t3 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if($tmp[$i]);
}
$t4 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(@$tmp[$i]);
}
$t5 = microtime(true);

echo 'Isset: '.($t2-$t1).'<br />';
echo 'Array Key Exists:'.($t3-$t2).'<br />';
echo 'Neither:'.($t4-$t3).'<br />';
echo 'Neither w/ @:'.($t5-$t4).'<br />';

?&gt;

El Forum

[eluser]BrianDHall[/eluser]
@Chad Fulton, thanks so much for your performance test and insight. I tried a slightly expanded and modified version:

Code:
$num =100000;
$tmp = array();

for ($i=0;$i<($num/2);$i++) {
    $tmp[$i]= "simple element $i";
}
reset($tmp);

ini_set('display_errors', 0);
error_reporting(0);


$t1 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(isset($tmp[$i]) && $tmp[$i]);
}
reset($tmp);
$t2 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(array_key_exists($i,$tmp) && $tmp[$i]);
}
reset($tmp);
$t3 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if($tmp[$i]);
}
reset($tmp);
$t4 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(@$temp[$i]);
}
reset($tmp);
$t5 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(empty($tmp[$i]));
}
reset($tmp);
$t6 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(! empty($tmp[$i]));
}
reset($tmp);
$t7 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(true);
}
reset($tmp);
$t8 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if($temp[$i]);
}
reset($tmp);
$t9 = microtime(true);
for ($i=0;$i<$num;$i++) {
    if(@$temp[$i]);
}
reset($tmp);
$t10 = microtime(true);

echo 'Isset: '.($t2-$t1).'<br />';
echo 'Array Key Exists: '.($t3-$t2).'<br />';
echo 'Neither: '.($t4-$t3).'<br />';
echo 'Neither w/ @: '.($t5-$t4).'<br />';
echo 'Empty: '.($t6-$t5).'<br />';
echo 'NOT Empty: '.($t7-$t6).'<br />';
echo 'Boolean: '.($t8-$t7).'<br />';
echo 'Cause Errors: '.($t9-$t8).'<br />';
echo 'Cause Errors w/ @: '.($t10-$t9).'<br />';

Note: All results are shown as a floating point in seconds.

Localhost WAMP:
Quote:Isset: 0.088625907897949
Array Key Exists: 0.4364640712738
Neither: 2.6649811267853
Neither w/ @: 5.2826218605042
Empty: 0.070709943771362
NOT Empty: 0.084458112716675
Boolean: 0.073637962341309
Cause Errors: 5.3429410457611
Cause Errors w/ @: 5.3846030235291

Live LAMP server:
Quote:Isset: 0.0335500240326
Array Key Exists: 0.0874769687653
Neither: 0.855342149734
Neither w/ @: 1.74126601219
Empty: 0.0294389724731
NOT Empty: 0.0313649177551
Boolean: 0.0208189487457
Cause Errors: 1.76280021667
Cause Errors w/ @: 1.82197380066

Wow, those last two I added are calling to a non-existent variable $temp instead of $tmp.

Indeed, it is clear the performance effect of errors (here adding as much as 1-2 seconds of run time on a single page access just to deal internally with the generated errors, or even just chance of errors). Now add that to a slightly busy site on a slightly busy server, and the fact that this is a basic coding convention that might be done dozens of times in any given function, with dozens of function calls, etc etc - that could add up DAMN quick, especially when dealing with arrays of increasing size and complexity!

As much as I like the perl-like single character use instead of a function call, here the performance is too big for even me to ignore - going back to empty() and isset() Smile

Indeed, it appears even asking if() to evaluate anything other than a boolean has a significant performance impact when looping through an array - even if you KNOW the variable is initialized!

El Forum

[eluser]BrianDHall[/eluser]
[quote author="Colin Williams" date="1259068309"]Second, don't confuse the wise goal of writing the least amount of code necessary with the not-so-wise goal of using the least amount of characters of code possible.[/quote]

This is true - one must limit the worthy goal of laziness and keep it from becoming complete and utter sloth Smile

El Forum

[eluser]BrianDHall[/eluser]
[quote author="John_Betong" date="1259062666"]&nbsp;

Your question certtainly is food for thought

From my limited experience I endeavour to write code without any errors because
the application seems to be much more nippier if there are no errors.

Using isset(...) I think is a KLUDGE because one is relying on PHP to trot off and
search to see if the variable has been set is in the boolean, numeric, string, array, etc
list of variables. After eliminating all possible options isset(...) will return your query.
This must take processing time.

I would be tempted to ensure your $potential_element has a default value and
therefore eliminate the search. Is it possible set a default value and run tests
with and without the default variable?
&nbsp;
&nbsp;
&nbsp;
edit: spelling.[/quote]

The performance test does seem to strongly support that less errors automatically equals a faster program, so it isn't a question of imagination.

Now, I had thought isset() would require considerably less work than it seems to, but you point out perhaps precisely why - it might very well run multiple type checks before returning. I'm just surprised empty() doesn't require even more work, but go figure.

Well, the problem with default value is in this particular instance it is loading arrays not only with files from the file system, but then parsing out bits of the filename into an array, which is then formed into an associative array. Its meant to be a very easy to use, slightly magical, banner ad system. Its complicated by the fact that the program needs to know about the contents of the file before ever opening it due to its interaction with another far larger array (200-1000+ multi-dimensional arrays itself) - but ultimately I have no idea what could be in any of the elements before the application is run.

That's rather why I'd never really considered this before - never really had a need to blindly walk through a significant sized array, but this extreme example helped to expose some underlying assumptions.