Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

PHP "short-circuit" logical evaluation...

Status
Not open for further replies.

shadedecho

Programmer
Oct 4, 2002
336
US
OK, my question is two fold:

1. Is there a way to force PHP to disable its normal "short-circuit" evaluation of logical conditionals (ie, x1 || x2 || x3 stops evaluating immediately if it finds a "true")? Can this disabling be done inline (for a particular script or even for a particular part of a script, so as to not affect the performance of other parts, while still giving me the more necessary behavior in a specific spot) instead of globally across the whole distribution?

2. If there is not a way to force PHP to behave differently, then what I need is to find the logical equivalent of || using only "xor" and "!". Why, you ask? because I need the BEHAVIOR of "||" but without the "short-circut", and "xor" and "!" by definition are NOT "short-circuit", meaning they both HAVE TO evaluate the expression to the right of them, no matter what.

the logical table would be:

Code:
 a    b  |  a || b  |  a ^ b  |  ?????  |
___  ___ | ________ | _______ | _______ |
 0    0  |     0    |    0    |    0    |
 0    1  |     1    |    1    |    1    |
 1    0  |     1    |    1    |    1    |
 1    1  |     1    |    0    |    1    |

any thoughts?
 
You are fighting one of the standard optimizations built into nearly every modern interpreter and compiler. And I don't know whether the optimization was deliberately built into PHP or is a side-effect of the fact that the PHP engine is written in c-language. So you might have to rewrite c to take out that optimization.

It sounds to me like you're trying to get PHP to invoke multiple functions inside logical operations. If this is the case, I recommend that you invoke all the functions, record the returns, and then peform the logic. Want the best answers? Ask the best questions: TANSTAAFL!
 
Actually, I'm fairly certain C has seperate Logic and Short Circuited Logic commands... Namely | for OR and || for ShortCircuited Or... & and && for the And's respectively... might be worth giving those a try.

Otherwise, Sleipnir's workaround is 100% effective, and probably even a little more readable.

-Rob
 
skiflyer:

That's incorrect. The c-language (and PHP) "&" and "|" operators are not equivalent to "&&" and "||", respectively.

The former operators are bitwise operators. The latter are logical operators. Want the best answers? Ask the best questions: TANSTAAFL!
 
The former are definately bitwise operators, but I thought they were overloaded as well... and I know they're not equivalent, I was saying they were the non-short circuited version...

I could well be wrong, I know in some language or another, that's certainly the case, and I'm also 90% C has a way to run logical comparisons without short circuiting... unfortunately at work I don't have access to all the things I need... perhaps I'll see if I can find the right notes for this this weekend.

-Rob
 
So here's my C program I just tested with GCC


#include "stdio.h"

int blah() {
printf("hello\n");
return 0;
}

int blah2() {
printf("world\n");
return 0;
}

main() {
printf("\n");

if (blah() & blah2()) {
printf("hey hey\n");
}
}

and the output is
hello
world

So, now I'm quite certain the & is the non-short circuited logical and.

-Rob
 
To answer the question, I just tested this in PHP and it holds true there as well... this program in PHP ran both functions.

Code:
<?php
function blah() {
  echo &quot;hello &quot;;
  return false;
}

function blah2() {
  echo &quot;world&quot;;
  return false;
}

if (blah() & blah2()) {
  echo &quot;hey hey&quot;;
}
?>

-Rob

p.s. I still think sleipnir's original workaround is a cleaner solution, much less obfuscated IMO.
 
sleipnir214-
I am trying to get two &quot;assignment&quot; operations to occur in a single (compound) boolean test. Individual assignment operations inside boolean tests are quite common:

Code:
$result = mysql_query(&quot;SELECT * FROM `table1`&quot;);
while ($row = mysql_fetch_array($result)) {
  // do something with $row
}

That iterates the while loop until the result set is empty, in which case the &quot;mysql_fetch_array()&quot; function returns a &quot;null&quot;, and the assignment operation to $row &quot;fails&quot;, basically returning the equivalent of false, and the while-loop stops as desired.

I am trying to do almost exactly this, except, I need to actually &quot;while-loop&quot; over 2 db result sets simultaneously.

Why? Because I am trying to get a result set that has no repeat records, but the source is from two tables, with the tendency to have many identical records in each.

[aside: IF i had mysql 4.0+, I could use UNION to union the two result sets in one SQL query, and run a &quot;GROUP BY&quot; on the whole query, eliminating the need for fancy coding. Alas, I have only 3.23,

Consider this: table A returns me 100 records, and table B returns me 100 records. In that combined result set, I would have 200 records, but say 110 of them were identical, leaving only 90 unique records. Of the 110 that were identical, lets say that that 60 of them were records present in BOTH table's result sets. That would then mean the remaining 50 &quot;identical&quot; records were actually just repeats of a record in the same table, respectively.

If I loop through result set #1 first, then result set #2, I had to go through 200 loop iterations to get my 90 unique records.

First things, first. I will specify &quot;DISTINCTROW&quot; (or alternately, GROUP BY) in both SQL queries, so I elminate those 50 &quot;same-table repeats&quot;. Now, I've pared my loop iteration count down to 150. However, I am still trying to only get the 90 records, and have to loop 150 times to do it.

If I could loop through both sets simultaneously, in effect checking in a single IF statement one of the 75 unique records from table A and one of the 75 unique records from table B to see if either record is already recorded in my &quot;unique result set&quot;, then my loop iteration count was cut down (in this perfect case) to only 75 (from 150), to get my 90 unique records!

so my ideal while-loop would look something like:

Code:
while ( ($r1 = mysql_fetch_array($result)) || ($r2 = mysql_fetch_array($result2))) ) {
 // check both result sets simultaneously, and add any
 // unique records to, say, an array called &quot;uniqueset&quot;.
}

The while-loop needs to execute an iteration if EITHER of the 2 assigment tests was successful, stopping when both result sets have been exhausted.

The snare here is that with short-circuiting, the || used above will stop if a result is found in the first assignment, so this would basically loop through the whole first result set until it started returning false, THEN it would start looping through the second result set until it was done. This in effect cancels the &quot;simultaneous&quot; nature of my while loop.

To boil it all down, i am trying to forgoe IN THIS SPECIAL CASE the optimization of &quot;short-circuting&quot; to make a VERY optimized version of my iterative code.
 
I'm not sure if you posted that while I was posting or not... but the answer to your query is to use

|

instead of

||

-Rob
 
skiflyer:
&quot;&&quot; is bitwise-and, not non-optimized logical-and. Check the actual values returned by the operators and read your c-language documentation.

Since the &quot;&&quot; and &quot;|&quot; (in both c-language and PHP) both operate on bits, then the engine must fetch both operands in order to know how to mathematically (at the bit-level) combine the two.

all:
If this code is being used in the condition of a conditional statement, then you can use &quot;&&quot; to check the two and it will work like &quot;&&&quot;.

If it is being used through an assignment statement to a variable and then that variable is being checked against TRUE, then the behavior will not be what he expects.

For example:
Code:
<?php
print '<pre>';

$a = 3;
$b = 5;

$c = $a || $b;
$d = $a | $b;

print '$c=' . $c;

print &quot;\n&quot;;

print '$d=' . $d;

print &quot;\n&quot;;

if ($c == TRUE)
{
	print '$c equal to TRUE';
}

print &quot;\n&quot;;

if ($d == TRUE)
{
	print '$d equal to TRUE';
}

print &quot;\n&quot;;

if ($c === TRUE)
{
	print '$c is exactly TRUE';
}
print &quot;\n&quot;;

if ($d === TRUE)
{
	print '$d is exactly TRUE';
}

?>

shadeecho:
Using a &quot;|&quot; as skiflyer has suggested will work. However, you are going to run into a couple of problems...

Since you will try to fetch from both result sets every time, when one result set runs out of records befor the other, the fetch operation on the result set with fewer records will return either an error or a warning each time. Your code may bomb if its an error.

If you are matching rows by fetching from both tables at once, you are assuming that all records fall into the same place in both tables. This is not guaranteed.

What exactly are you trying to find between the two tables? Records that are in one but not the other? Or records that appear in both tables? Want the best answers? Ask the best questions: TANSTAAFL!
 
So in a language (C), where 0 represents false, and all other values represent true.... what's the functional difference between a bitwise AND and a logical AND?

Even PHP has this functionallity, until you start using the === operator as your example demonstrated... but C, not having this distinction, turns out the same, no?

-Rob
(Going to read up more on the === operator as he's reminded of Scheme and the differences between eq and equal... is that all we're talking about here?)
 
Correction.

shacedecho:
Ignore my first reservation. mysql_fetch_*() (at least mysql_fetch_assoc()) does not return a warning or an error if you fetch past the end of the result return.

Another thing occurred to me...

If you use mysql_num_rows() against both result handles, you could then loop through the larger number in a for-loop, fetching from both result handles each time.

Code:
<?php
   .
   .
   .
$rh1 = mysql_query(...);
$rh2 = mysql_query(...);

$max_out = ($rh1 > $rh2) ? $rh1 : $rh2;

for ($counter = 1; $counter <= $max_out; $counter++)
{
   $rh1 = mysql_fetch_array($rh1);
   $rh2 = mysql_fetch_array($rh2);
   .
   .
   .
}
?>
Want the best answers? Ask the best questions: TANSTAAFL!
 
Just another question which really sums up what I mean to say...

I have always been under the impression from combining logic and engineering texts, that a bitwise comparison is the computer implementation of a logical comparison... i.e. a transistor implementation of the logic gates. Given that, I'm a bit confused by exactly what you mean when you say a bitwise comparison is not the same as a non-optimized logical comparison...

sorry if I'm seeming dense, I love playing with logic, and want to understand this better.

-Rob
 
This test code chunk is more appropriate to what I am trying to accomplish:

Code:
function blah() {
  echo &quot;hello<br>&quot;;
  return &quot;from blah()<br>&quot;;
}

function blah2() {
  echo &quot;world<br>&quot;;
  return &quot;from blah2()<br>&quot;;
}

$a = &quot;&quot;;

if ( ($a .= blah()) | ($a .= blah2()) ) {
  echo $a;
}

This returns the following output:

hello
world
from blah()
from blah2()

As is desired.

Now that I think about this, and THANK YOU!!! to all those who have posted today to help this come to light, this makes total sense... Using the bitwise operators, what is occurring is the bitwise-level operation on the result of two non-bitwise operations (the assignments). If a non-bitwise operation returns null, the bitwise operator would bind to that (or more appropriately, would &quot;see&quot; it) as a &quot;0&quot;, otherwise it would see a &quot;1&quot;.

The if statement above is doing a bitwise | on (in this case) two &quot;1&quot;s, so it returns a &quot;1&quot; which is then reinterpreted by the &quot;if&quot; statement as TRUE!

So, in this special case where you are trying to string together non-short-circuited boolean tests, these operators work on their operands at the bitwise level, and since they are not shortcircuited, the behavior is a non-short-circuited &quot;boolean test&quot;.
 
skiflyer:

Suppose $a = 1 and $b = 2;

If you perform $c = $a || $b, the result in $c will be 1 (in both PHP and c). Logical operators return either FALSE (usually 0) or TRUE (usually non-zero). Since it only is returning a logical result, it only cares about non-zero values as TRUE and zero values as FALSE. The operations on &quot;||&quot; and &quot;&&&quot; can be optimized (not &quot;short-circuited&quot;).

But if my code performs $c = $a | $b, then the &quot;|&quot; operator peers into the individual bits of the operands:

(assume 4-bit numbers. It's less typing)
Code:
$a   $b   $c
0    0    0
0    0    0
0    1    1
1    0    1

In this case $c is assigned the value of 3, not 1.





Want the best answers? Ask the best questions: TANSTAAFL!
 
sleipnir214, more to the point of your demonstration:

skiflyer, consider this:

Code:
$a = 3;
$b = 0;

$c = $a || $b;
$d = $a | $b;

echo &quot;c=&quot;.$c.&quot;<br>&quot;;
echo &quot;d=&quot;.$d.&quot;<br>&quot;;

if ($c) {  // aka if ($c == true)
 echo &quot;c is true<br>&quot;;
}
if ($d) {
 echo &quot;d is true<br>&quot;;
}
if ($c === true) {
 echo &quot;c is exactly true<br>&quot;;
}
if ($d === true) {
 echo &quot;d is exactly true<br>&quot;;
}

One would expect to see:

c=1
d=3
c is true
d is true
c is exactly true

However, when I execute this, I don't get that last line of &quot;c is exactly true&quot;. This is very curious to me, I would have thought that the natural result of the || would be a pure &quot;true&quot; or a pure &quot;false&quot;, such that the === equivalency test against &quot;true&quot; would itself be true.

So, then I thought that maybe the datatype of the operands to a logical operator affected the datatype of the result, so that if i changed $a=true and $b=false, and reran the script, surely I would get the &quot;c is exactly true&quot;. I did not.

SO, even though we are on a truely different line of questions now, I am very confused as to why the &quot;=== true&quot; test does not work if the assignment to the variable is the result of a logical operator like ||. If I just manually set $c = true, and then test for &quot;$c === true&quot;, it does work.

(btw, STILL doesn't work if you switch it around, and do $c = $a && $b, and then test for $c === false).
 
sleipnir:

Ok, so I think we now have two discussions going... the second one first if you don't mind...

$c = 3 or $c = 1, both are non-zero and are so considered true. Regardless of anything, that's the important question we're asking, and how it should be interpretted. In some ways $c = 3 is actually carrying more information, unfortunately to us, it's not important information.... and in fact often will be non-useful as it's not a 1 to 1 map back to the question.

Now as to the optimization/short-circuited conversation... you're basically just saying that since we're only asking for true or false, it answers with 0/1 as soon as it's able, rather than mixing all the bits and giving the &quot;full information&quot; answer?

The side effect of this lesser requirement is what leads to the short-circuit effect, namely, not even executing the later conditionals once a value of 0/1 can be determined.

No response needed unless I'm just incorrect, just thinking out loud to make sure I got it.

-Rob
 
We are talking about a logical condition, here. The primary goal is to figure out as efficiently as possible whether that condition evaluates to TRUE or FALSE, not to invoke functions.

If I can determine the evaluation of the condition without executing all the functions invoked in the condition, then my code is more efficient. A side-effect of this performance optimization is that an unaware programmer can come across unexpected behavior -- but then this is the case in a lot of other ways, too.


The difference between &quot;&&&quot; and &quot;&&quot; is not just in the return -- it's also in how the operators interpret their operands. &quot;&&&quot; will examine both entire values, and treat each as a single TRUE or FALSE value. &quot;&&quot; operates on the individual bits of both operands. That is why &quot;&&quot; must always evaluate both operands -- it must know both their bit-patterns in order to operate on them. A common use of bitwise-logical operators is to write very efficient mathematical operations.

And suppose that instead of using the value in $c in an if-statement, I were using $c as the condition of a switch? In that case, it makes a large difference whether the value is 1 or 3.

As a real-world example, take the case of the IP stack. The IP stack determines whether a packet is destined for the local network by performing a bitwise-XOR on the packet's destination-IP address and the computer's local IP address, then bitwise-ANDs that result with the network mask. In PHP:

Code:
(($local_ip ^ $destination_ip) & $network_mask)

If the end result is all zeros, then the packet is bound for the local network. If not, it is bound for another network and should be sent to the network gateway.

It doesn't matter if the last judgement made by the IP stack is whether the result is zero or not. It is necessary to perform the logical operations on the bit-level -- if you performed the operations on the whole number as a single value, unless the network mask were 0.0.0.0 no packet could ever be determined to be destined for the local network. Want the best answers? Ask the best questions: TANSTAAFL!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top