After the trouble I had with bitwise operators yesterday I found some time to really sit down and get my head properly around them. Let’s dive straight in.
We need to initially define our flags:
define('BASE', 0); // binary 00000000 define('F1', 1); // binary 00000001 define('F2', 2); // binary 00000010 define('F3', 4); // binary 00000100
To start with we have no flags set, so if we set F1 using the following:
$f1_set = BASE + F1; // $f1_set = 1 echo "F1 set = $f1_setn";
All is well and good, $f1_set = 1 as expected.
However, what if we set F1 again?
$f1_set_twice = $f1_set + F1; // $f1_set_twice = 2 !!! wrong !!! echo "F1 set twice = $f1_set_twicen";
As you can see, if we set F1 twice, it effectively “unsets” F1 and sets F2. Not what we were after.
So why is this? Well, it’s kind of obvious and I was being a bit of a muppet for not spotting it yesterday. The reason is pretty simple: 1 + 1 = 2. (I told you it was obvious!)
Clearly this is not what we want, but how can we solve this? By using the bitwise OR. If we change the statements slightly as follows:
$f1_or_set = BASE | F1; // $f1_or_set = 1 echo "F1 OR set = $f1_or_setn"; $f1_or_set_twice = $f1_or_set | F1; // $f1_or_set_twice = 1 - huzzah! echo "F1 OR set twice = $f1_or_set_twicen";
As far as “unsetting” the flags if we use my original method we fall (again) into trouble.
$f1_and_f3 = BASE | F1 | F3; // $f1_and_f3 = 5; $unset_f3 = $f1_and_f3 - F3; // $unset_f3 = 1 echo "Unset F3 = $unset_f3n"; $unset_f1 = $f1_and_f3 - F1; // $unset_f1 = 4; echo "Unset F1 = $unset_f1n";
Now, if we try to “unset” F1 twice, we arrive at the problem.
$unset_f1 = $f1_and_f3 - F1; // $unset_f1 = 3; echo "Unset F1 = $unset_f1n";
Unsetting F1 twice here effectively turns off F3 and sets F1 and F2 – completely wrong!
Instead, if we use the &~ binary operator mentioned in Jesper’s comment all works as expected. (note: I can’t find mention of this operator in the PHP docs, please someone help me out)
$f1_and_f3 = BASE | F1 | F3; // $f1_and_f3 = 5; $unset_f3 = $f1_and_f3 &~ F3; // $unset_f3 = 1 echo "Unset F3 = $unset_f3n"; $unset_f1 = $f1_and_f3 &~ F1; // $unset_f1 = 4; echo "Unset F1 = $unset_f1n";
Even if we try to “unset” a flag twice, it still has the same results:
$unset_f1 = $f1_and_f3 &~ F1; // $unset_f1 = 4; echo "Unset F1 = $unset_f1n"; $unset_f1_twice = $unset_f1 &~ F1; // $unset_f1_twice = 4; echo "Unset F1 twice = $unset_f1_twicen";
Also in Jesper’s comment and original post was the use of the left shift operator: <<. After playing around with this it seems very simple to use, as follows:
$f1 = 1; // 00000001 $f2 = 1<<1; // 00000010 $f3 = 1<<2; // 00000100 $f4 = 1<<3; // 00001000
Or to put it another way:
$f1 = 1; // 00000001 $f2 = $f1<<1; // 00000010 $f3 = $f2<<1; // 00000100 $f4 = $f3<<1; // 00001000
After all this I think I am a little closer to understanding Bitwise operations, hopefully! Tomorrow I’ll have a crack at testing to see if a flag is turned on or not. Until then…