I'm not familiar with bitwise operators, but I have seem them used to store simple settings before.
I need to pass several on/off options to a function, and I'd like to use a single integer for this. How can I go about setting and reading these options?
You sure can do it in PHP.
Let's say you have four booleans you want to store in a single value. That means we need four bits of storage space
0000
Each bit, when set individually, has a unique representation in decimal
0001 = 1 // or 2^0
0010 = 2 // or 2^1
0100 = 4 // or 2^2
1000 = 8 // or 2^3
A common way to implement this is with bit masks to represent each option. PHP's error levels are done this way, for example.
define( 'OPT_1', 1 );
define( 'OPT_2', 2 );
define( 'OPT_3', 4 );
define( 'OPT_4', 8 );
Then when you have an integer that represents 0 or more of these flags, you check with with the bitwise and operator which is &
$options = bindec( '0101' );
// can also be set like this
// $options = OPT_1 | OPT_3;
if ( $options & OPT_3 )
{
// option 3 is enabled
}
This operator works as such: only bits that are set in both operands are set in the result
0101 // our options
0100 // the value of OPT_3
----
0100 // The decimal integer 4 evaluates as "true" in an expression
If we checked it against OPT_2
, then the result would look like this
0101 // our options
0010 // the value of OPT_2
----
0000 // The decimal integer 0 evaluates as "false" in an expression
It works pretty much the same way in both languages, a side by side comparison:
C:
#include <stdio.h>
#include <stdint.h>
#define FLAG_ONE 0x0001
#define FLAG_TWO 0x0002
#define FLAG_THREE 0x0004
#define FLAG_FOUR 0x0008
#define FLAG_ALL (FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR)
void make_waffles(void)
{
printf("Yummy! We Love Waffles!!!\n");
}
void do_something(uint32_t flags)
{
if (flags & FLAG_TWO)
make_waffles();
}
int main(void)
{
uint32_t flags;
flags |= FLAG_ALL;
/* Lets make some waffles! */
do_something(flags);
return 0;
}
PHP:
<?php
define("FLAG_ONE", 0x0001);
define("FLAG_TWO", 0x0002);
define("FLAG_THREE", 0x0004);
define("FLAG_FOUR", 0x0008);
define("FLAG_ALL", FLAG_ONE|FLAG_TWO|FLAG_THREE|FLAG_FOUR);
function make_waffles()
{
echo 'Yummy! We Love Waffles!!!';
}
function do_something($flags)
{
if ($flags & FLAG_TWO)
make_waffles();
}
$flags |= FLAG_TWO;
do_something($flags);
?>
Note, you don't absolutely need to use constants, I just use them out of habit. Both examples will run, I compiled the C version via gcc -Wall flags.c -o flags
. Change flags
in either example to anything but FLAG_TWO
or FLAG_ALL
and (sadly) no waffles will be made.
In the C version, you don't have to tickle the preprocessor, it could quite easily be an enum, etc - that's an exercise for the reader.
quote
"the idea is not good, really. you would better pass few boolean. if you want use bitwise then
function someFunc($options)
{
if ($options & 1 != 0)
//then option 1 enabled
if ($options & (1 << 1) != 0)
//then option 2 enabled
if ($options & (1 << 2) != 0)
//then option 3 enabled
}
"
What you have done would be okay if you were checking for a single value, although not optimal, so checking that a bit is enabled, but lets say we wanted to be able to match any, or exact we could have the following methods
function matchExact($in, $match) { // meets your criterion, as would a switch, case, but ultimately not suited for use with flags
return $in === $match;
}
function matchAny($in, $match) { // meets original criterion with more lexical name however it returns true if any of the flags are true
return $in |= $match;
}
if you then wanted to expand upon this by having specific actions only happening if bit x,y,z was enabled then you could use the following
function matchHas($in, $match) { // more bitwise than === as allows you to conditionally branch upon specific bits being set
return $in &= $match;
}
I also think if you are doing what was done in the above quote, flags may not be the best idea, exact values might be better, which does have the benefit of allowing more discreet actions. (0-255) for 8-bit over 8 distinct flags
The whole reason flags work so well is because in base 2 "8" does not contain "4", and "2" does not contain "1".
________________________
|8|4|2|1|Base 10 Value |
------------------------
|1|1|1|1|15 |
|1|1|1|0|14 |
|1|1|0|1|13 |
|1|1|0|0|12 |
|1|0|1|1|11 |
|1|0|1|0|10 |
|1|0|0|1|9 |
|1|0|0|0|8 |
|0|1|1|1|7 |
|0|1|1|0|6 |
|0|1|0|1|5 |
|0|1|0|0|4 |
|0|0|1|1|3 |
|0|0|1|0|2 |
|0|0|0|1|1 |
|0|0|0|0|0 |
------------------------