Consider the following code:

<?php

function important_function( $i )
{
  printf ("<p>Called for the %sth time.</p>\n", $i);
  return ($i < 3);
}

function loop_with_and( )
{
  $ret = true;
  for( $i = 0; $i < 10; $i ++ )
  {
    $ret = $ret && important_function( $i );
  }
  return $ret;
}

function loop_without_and( )
{
  $ret = true;
  for( $i = 0; $i < 10; $i ++ )
  {
    if( ! important_function( $i ) )
    {
      $ret = false;
    }
  }
  return $ret;
}

?>

<div>
<p><b>Call with And</b></p>
<?php loop_with_and( ) ?>
</div>

<div>
<p><b>Call without And</b></p>
<?php loop_without_and( ) ?>
</div>

Can anyone give me a good reason why the output is:

<div>
<p><b>Call with And</b></p>
<p>Called for the 0th time.</p>
<p>Called for the 1th time.</p>
<p>Called for the 2th time.</p>
<p>Called for the 3th time.</p>
</div>

<div>
<p><b>Call without And</b></p>
<p>Called for the 0th time.</p>
<p>Called for the 1th time.</p>
<p>Called for the 2th time.</p>
<p>Called for the 3th time.</p>
<p>Called for the 4th time.</p>
<p>Called for the 5th time.</p>
<p>Called for the 6th time.</p>
<p>Called for the 7th time.</p>
<p>Called for the 8th time.</p>
<p>Called for the 9th time.</p>
</div>

To me, that makes no sense; I didn't instruct the loop to finish on the first encounter of a 'false'; if that's what I'd wanted; I'd have put in a conditional break. All I can assume is that this is some kind of bug (where the loop continuation becomes dependant on the validity/return from an inner statement), or that this might actually be some kind of deliberate loop optimization attempt (which I'd consider to be thoroughly stupid; given that I'm collecting a single validity result from many unrelated , but essential, validation functions )...

Either way; can anyone test the same code on PHP5 for me? My hosting plan uses PHP 4.4.6.

It's just a case of lazy evaluation. if $ret is false, then $ret = $ret && foo() will never evaluate foo. The loop will execute 10 times, but the function call only as long as $ret is true.

Comments
Thanks for the tip.

Hm.. That's a bit too lazy for my liking.. it certainly took a lot of messing around to diagnose that type of statement as being the error cause!

I thought this might be a PHP thing, but apparently not; the same applies in C++ aswell.

Swapping the two operands to && around seems to have the desired results; in PHP and C++.

Thanks for the info.

The thing you experienced is also known as Short Circuit Evaluation. If you want to find a way around it, try using the logical version of Bitwise AND. Something like this: $ret = $ret & foo(); //notice single & This way you can be always sure that the second part is evaluated irrespective of the outcome of the first one. Though normally in such situations, the preferred way would be to place the return value of the function in another variable and use it in the real equation.

Isn't & bitwise and && logical? It is that way in C/++ as far as I know; but is it different in PHP? What about 'and'; is that another way of writing '&', '&&' or something else?

I would think that bitwise would be more likely to work as I'd like it to; since a bitwise AND is always dependant on a myriad of true / falses; thus, the value can never be determined from either operand alone... Saying that; one case : 00..0 bitAnd f() is always going to evaluate to 00..0, and that's a case I want to be fully evaluated..

I wouldn't like to risk unpredictability due to trying to one-line as much as I can; it's already a waste of process to AND on each iteration.

For now, I have gone with if(!validate($i)){$ret=false;} but I suppose I could go with:

validate( $i ) or ( $ret = false );

Using a short-circuit to my advantage; I can see it more clearly there.

Thanks for reading and replying both. I distrust PHP for some valid reasons, this would have been an invalid one; but finding this nearly drove me to move the related project into another language for want of not crashing into more weirdness.

However; I can't really argue with C++'s interprettation of the same construct.

using if(!validate($i)) $ret=false; makes the intent of the code obvious. Hackish code using lazy evaluation for an assignment is very poor style IMHO.

Also, the logical operators and and or are both logical, but have lower precedence than pretty much everything else (here is a list of operator precedences, only the comma is below these).

This question has already been answered. Start a new discussion instead.