|
|
 |
Bitwise operators allow you to turn specific bits within an
integer on or off. If both the left- and right-hand parameters are
strings, the bitwise operator will operate on the characters' ASCII
values.
Table 15-3. Bitwise Operators | Example | Name | Result |
|---|
| $a & $b | And | Bits that are set in both $a and $b are set. | | $a | $b | Or | Bits that are set in either $a or $b are set. | | $a ^ $b | Xor |
Bits that are set in $a or $b but not both are set.
| | ~ $a | Not |
Bits that are set in $a are not set, and vice versa.
| | $a << $b | Shift left |
Shift the bits of $a $b steps to the left (each step means
"multiply by two")
| | $a >> $b | Shift right |
Shift the bits of $a $b steps to the right (each step means
"divide by two")
|
| Warning |
Don't right shift for more than 32 bits on 32 bits systems. Don't left shift
in case it results to number longer than 32 bits.
|
User Contributed Notes
Bitwise Operators
Séb.
19-Apr-2005 09:28
Another practical case...
<?php
header('Content-Type: text/plain') ;
$x = 9124 ;
$n = 1 ;
while ( $x > 0 ) {
if ( $x & 1 == 1 ) {
echo $n, "\n" ;
}
$n *= 2 ;
$x >>= 1 ;
}
?>
Séb.
04-Mar-2005 05:13
A bitwise operators practical case :
<?php
$color = 0xFEA946 ;
$red = $color >> 16 ;
$green = ($color & 0x00FF00) >> 8 ;
$blue = $color & 0x0000FF ;
printf('Red : %X (%d), Green : %X (%d), Blue : %X (%d)',
$red, $red, $green, $green, $blue, $blue) ;
?>
icy at digitalitcc dot com
24-Feb-2005 01:24
Say... you really want to have say... more than 31 bits available to you in your happy bitmask. And you don't want to use floats. So, one solution would to have an array of bitmasks, that are accessed through some kind of interface.
Here is my solution for this: A class to store an array of integers being the bitmasks. It can hold up to 66571993087 bits, and frees up unused bitmasks when there are no bits being stored in them.
<?php
DEFINE('INTEGER_LENGTH',31); class bitmask
{
protected $bitmask = array();
public function set( $bit ) {
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] |= 1 << $bit;
}
public function remove( $bit ) {
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] &= ~ (1 << $bit);
if(!$this->bitmask[$key])
unset($this->bitmask[$key]);
}
public function toggle( $bit ) {
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
$this->bitmask[$key] ^= 1 << $bit;
if(!$this->bitmask[$key])
unset($this->bitmask[$key]);
}
public function read( $bit ) {
$key = (int) ($bit / INTEGER_LENGTH);
$bit = (int) fmod($bit,INTEGER_LENGTH);
return $this->bitmask[$key] & (1 << $bit);
}
public function stringin($string) {
$this->bitmask = array();
$array = str_split( strrev($string), INTEGER_LENGTH );
foreach( $array as $key => $value )
{
if($value = bindec(strrev($value)))
$this->bitmask[$key] = $value;
}
}
public function stringout() {
$string = "";
$keys = array_keys($this->bitmask);
sort($keys, SORT_NUMERIC);
for($i = array_pop($keys);$i >= 0;$i--)
{
if($this->bitmask[$i])
$string .= sprintf("%0" . INTEGER_LENGTH . "b",$this->bitmask[$i]);
}
return $string;
}
public function clear() {
$this->bitmask = array();
}
public function debug() {
var_dump($this->bitmask);
}
}
?>
It treats a positive integer input as a bit, so you don't have to deal with the powers of 2 yourself.
<?php
$bitmask = new bitmask();
$bitmask->set(8979879); $bitmask->set(888);
if($bitmask->read(888))
print 'Happy!\n';
$bitmask->toggle(39393); $bitmask->remove(888);
$bitmask->debug();
$bitmask->stringin("100101000101001000101010010101010
00000001000001");
print $bitmask->stringout() . "\n";
$bitmask->debug();
$bitmask->clear();
$bitmask->debug();
?>
Would produce:
Happy!
array(2) {
[289673]=>
int(65536)
[1270]=>
int(8388608)
}
0000000000000001001010001010010001010100101010100
0000001000001
array(2) {
[0]=>
int(355106881)
[1]=>
int(37970)
}
array(0) {
}
louis /at/ mulliemedia.com
20-Jan-2005 04:12
Note that the ^ operator, unlike in some other languages, is *not* the same as the pow() function.
dasch at ulmail dot net
14-Dec-2004 02:06
The function below can be rewritten to this:
<?php
function setflag (&$var, $flag, $set = true)
{
$var = $set ? ($var | $flag) : ($var & ~$flag);
}
?>
Gemini_5
07-Sep-2004 05:06
Extending the comment by andrew at thepenry dot net:
His (slightly altered) function for reference:
<?php
function setflag(&$var, $flag, $set) {
if (($set == 1)) $var = ($var | $flag);
if (($set == 0)) $var = ($var & ~$flag);
return;
}
?>
You could shorten the operations to:
<?php
$var |= $flag; $var &= ~$flag; ?>
Which kind of eliminates the need for a function.
rdewaard at DONTSPAMME dot xs4all dot nl
14-Apr-2004 02:36
@jbrand1 at uwe_emm_bee_see dot edu:
"I have to be careful when I add roles so that it's 2^whatever. I wish I knew how to change the auto_increment so that it was just a bit shift."
If you store your roles in MySQL you might consider NOT using a table 'roles' but adding a field 'roles' to the table 'user' using the datatype SET for that field. The name for each role will have a decimal value 2^whatever! See http://www.vbmysql.com/articles/mysqlsetdatatype.html for details and ideas.
temp dot 2004-03-27 at fobit dot de
28-Mar-2004 01:58
Always use type casting!
<?php
echo (134 & 512) . "\n";
echo ("134" & "512") . "\n";
echo ((int)"134" & (int)"512") . "\n";
?>
Output:
0
110 (wrong!)
0
lars dot jensen at ljweb dot com
25-Mar-2004 05:27
A useful set of functions, making it easier to play with bits as a set of booleans. $bit is a integer between 1 and 32 representing the position of the bit you wish to manipulate.
<?php
function setbit($val, $bit) {
if (readbit($val, $bit)) return $val;
return $val += '0x'.dechex(1<<($bit-1));
}
function clearbit($val, $bit) {
if (!readbit($val, $bit)) return $val;
return $val^(0+('0x'.dechex(1<<($bit-1))));
}
function readbit($val, $bit) {
return ($val&(0+('0x'.dechex(1<<($bit-1)))))?'1':'0';
}
function debug($var, $bitlength=32) {
for ($j=$bitlength;$j>0;$j--) {
echo readbit($var, $j);
if ($j%4 == 1) echo ' ';
}
}
?>
andrew at thepenry dot net
26-Feb-2004 04:11
Here is a useful function for setting flags (or bits). It takes three paramaters:
1. $var This is the variable whose flag you want to set
2. $flag This is the flag(s) you want set
3. $set This is either ON or OFF
Simply adding or subtracting a flag is not a good idea. For example, if the variable is in binary 0101 (thats 5 in dec) and you want to turn the 1 bit off subtracttion would work. 5-1=4 which would be 0100. However, the variable is 0100, and you want subtracted, 4-1 =3 or 0011. What you intended to do was to turn off the one bit regardless of whether it is on or off.
This function will turn on or off a bit(or bits) regardless of the bit's current state.
<?php
define('ON', 1);
define('OFF', 0);
function setflag(&$var, $flag, $set=ON ) {
if (($set == ON)) $var = ($var | $flag);
if (($set == OFF)) $var = ($var & ~$flag);
return;
}
?>
For a full test of this try this code:
<pre>
<?php
define ('f1', 1);
define ('f2', 2);
define ('f4', 4);
define ('f8', 8);
define('ON', 1);
define('OFF', 0);
function setflag(&$var, $flag, $set=ON ) {
if (($set == ON)) $var = ($var | $flag);
if (($set == OFF)) $var = ($var & ~$flag);
return;
}
function dbi($var){ $output = ($var & f8) ? '1' : '0';
$output .= ($var & f4) ? '1' : '0';
$output .= ($var & f2) ? '1' : '0';
$output .= ($var & f1) ? '1' : '0';
return $output;
}
$var1 = ( f8 | f2 | f1 );
echo (" " . dbi($var1). "\n" );
echo ("ON " . dbi(f4). "\n" );
setflag($var1, f4, ON);
echo ("IS " . dbi($var1). "\n" );
echo ("\n");
$var2 = ( f8 | f2 | f1 );
echo (" " . dbi($var2). "\n" );
echo ("OFF " . dbi(f1). "\n" );
setflag($var2, f1, OFF);
echo ("IS " . dbi($var2). "\n" );
echo ("\n");
echo ("\n");
$var3 = ( f8 | f2 | f1 );
echo (" " . dbi($var3). "\n" );
echo ("ON " . dbi((f4 | f1)). "\n" );
setflag($var3, (f4 | f1), ON);
echo ("IS " . dbi($var3). "\n" );
echo ("\n");
$var4 = ( f8 | f2 | f1 );
echo (" " . dbi($var4). "\n" );
echo ("OFF " . dbi((f4 | f1)). "\n" );
setflag($var4, (f4 | f1), OFF);
echo ("IS " . dbi($var4). "\n" );
echo ("\n");
?>
</pre>
Which returns:
1011
ON 0100
IS 1111
1011
OFF 0001
IS 1010
1011
ON 0101
IS 1111
0100
OFF 0101
IS 0000
crizza at sdf dot lonestar dot org
25-Feb-2004 01:27
Personally I get a great deal of pleasure using bitwise operators for dealing with flags
here is an example using c code because im a php noob, but the principal of bitwise ops is the same.
#define F_WITH_DEBUG 0x01
#define F_WITH_HELP_INFO 0x02
#define F_BE_VERBOSE 0x04
unsigned short flags = 0;
flags |= ( F_WITH_DEBUG | F_WITH_HELP_INFO | F_BE_VERBOSE );
/* now the first byte of flags look like this 00000111 */
/* check the status of flags */
if( flags & F_WITH_DEBUG) {
/* do something */
}
flags are great, I love *them.
richard-slater.co.uk
22-Feb-2004 03:07
For those (like me) who are trying to do bit masking with very large numbers, here is a useful function to do the work for you.
<?php
function isBitSet($bitMask, $bitMap)
{
return (bool) gmp_intval(gmp_div(gmp_and($bitMask, $bitMap),$bitMask));
}
?>
tanstep at hotmail dot com
17-Feb-2004 11:14
Note that precedence is wrong for bitwise and comparison operators:
use this:
if((pow(2, $pos) & $_bits) > 0)
NOT this:
if(pow(2, $pos) & $_bits > 0)
tanstep at hotmail dot com
17-Feb-2004 11:03
Beware that strings can be ANDed, XORed, and ORed just fine. When printing the results of a bitwise operation, results can be a bit confusing, since operands may be converted to strings BEFORE bitwise operator execution.
so, use this:
print('result: ');
print(pow(2, $pos) & $this->_bits);
print('<br>');
NOT this
print("result: " . pow(2, $pos) & $this->_bits . "<br>");
jbrand1 at uwe_emm_bee_see dot edu
04-Jan-2004 12:04
So here's an interesting little thing that I do with bitwise operators, could potentially be useful to others.
I have a roles based system which shows, among other things, menu entries based on a users role. The menu entries are stored in a mysql database as are the users and the roles.
My users table looks like this:
+----------+--------+
| Username | RoleID |
+----------+--------+
| admin | 1 |
| suser | 2 |
| user | 4 |
+----------+--------+
My roles table looks like this:
+-------------------------------+--------+
| RoleName | RoleID |
+-------------------------------+--------+
| System Administrator | 1 |
| Super User | 2 |
| User | 4 |
+-------------------------------+--------+
My menus table looks like this:
+-------------------+---------------------+---------+
| Menu_Title | MenuFile | Roles |
+-------------------+---------------------+---------+
| Data | Data.php | 7 |
| All Data | All_Data.php | 3 |
| Shutdown | Shutdown.php | 1 |
+-------------------+---------------------+---------+
Now, when the user logs in I run a select to see if they are valid:
SELECT * FROM Users WHERE Username = '<USER>' AND Password = '<PW>'
If no rows were returned I print an error message and redraw the login screen, otherwise I set some session variables:
<?php
$row = mysql_fetch_assoc($recordset);
$_SESSION['Username'] = $row['Username'];
$_SESSION['RoleID'] = $row['RoleID'];
?>
In my MainMenu.php I check that a valid user is logged in and if so I generate the menu:
<?php
if( isset($_SESSION['Username']) &&
isset($_SESSION['RoleID']) )
{
generateMenuEntries($_SESSION['RoleID']);
}
?>
<?php
function generateMenuEntries( $roleID )
{
$menuQuery = sprintf("SELECT * FROM menus WHERE (Roles & %d) = %d", $roleID, $roleID);
$menuRecordset = mysql_query($menuQuery, $myConn);
while( $menu = mysql_fetch_assoc( $menuRecordset ))
{
echo ' <tr>';
echo ' <td width="3%"> </td> ';
echo ' <td width="97%"><a href="'; echo $menu['MenuFile']; echo '">'; echo $items['Menu_Title']; echo '</a></td>';
echo ' </tr>';
}
}
?>
So the important part of the query is the (Roles & $roleID) = $roleID, that does a bitwise AND function and since only one bit is set in our role the value returned will either be 0 or the $roleID.
I know this isn't the most secure way to handle the user/session data but it's sufficient for my needs. I have to be careful when I add roles so that it's 2^whatever. I wish I knew how to change the auto_increment so that it was just a bit shift. I suppose I could use an auto-generated ID field and do a stored procedure that calculates the role as 2^ID. I'll have to look into it.
Hope this helps someone.
achim at gmeiner dot cc
27-Dec-2003 01:21
krang at krang dot org dot uk
04-Dec-2003 04:30
Hopefully this may help someone understand the fun of Bitwise Operators....
The purpose of this function is to return a value from the GPC (Get, Post and Cookie) and do some basic formatting to it depending on the $VALIDATION value:
<?PHP
function RETURN_SUBMITTED_VALUE ($VARIABLE, $METHOD, $VALIDATION) {
if ($METHOD == 'POST') {
if (!isset($_POST[$VARIABLE])) $_POST[$VARIABLE] = '';
$VALUE = $_POST[$VARIABLE];
} elseif ($METHOD == 'COOKIE') {
if (!isset($_COOKIE[$VARIABLE])) $_COOKIE[$VARIABLE] = '';
$VALUE = $_COOKIE[$VARIABLE];
} else {
if (!isset($_GET[$VARIABLE])) $_GET[$VARIABLE] = '';
$VALUE = $_GET[$VARIABLE];
}
if (ini_get ('magic_quotes_gpc') == true) {
$VALUE = stripslashes($VALUE);
}
if (($VALIDATION & 8) == 8) {
$VALUE = (int)$VALUE;
}
if (($VALIDATION & 4) == 4) {
$VALUE = strtolower($VALUE);
}
if (($VALIDATION & 2) == 2) {
$VALUE = strip_tags($VALUE);
}
if (($VALIDATION & 1) == 1) {
$VALUE = trim($VALUE);
}
return $VALUE;
}
echo RETURN_SUBMITTED_VALUE ('ID', 'GET', 8) . '<br />';
echo RETURN_SUBMITTED_VALUE ('NAME', 'GET', 3) . '<br />';
echo RETURN_SUBMITTED_VALUE ('GENDER', 'GET', 6) . '<br />';
?>
For those that don’t understand binary, the numbers you see are not random, they double each time (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024...) which allows you to mix and match the different function, eg...
1 + 2 = 3 (Trim Whitespace + Strip HTML)
2 + 4 = 6 (Strip HTML + Convert to lower case)
rob at thijssen dot co dot uk
27-Oct-2003 04:14
Well, every programmer already knows this but for anyone new to the fun here's some quick clean code to determine if a number is odd or even.
function oddeven($x){
if($x & 1) return "odd";
else return "even";
}
joel at alpsgiken dot gr dot jp
10-Oct-2001 03:58
All shifts are signed shifts. The shift is performed with the shift count modulo 32, which explains the behavior which looks like a bit rotation, mentioned above.
Example:
$bit = 0x20000000;
print "$bit << 5 == " . ($bit << 5) . $prod . "<br />";
print "$bit >> 5 == " . ($bit >> 5) . $prod . "<br />";
print "$bit >> 37 == " . ($bit >> 37) . $prod . "<br />";
$bit = 4;
print "$bit >> 5 == " . ($bit >> 5) . $prod . "<br />";
print "$bit << 5 == " . ($bit << 5) . $prod . "<br />";
print "$bit << 37 == " . ($bit << 37) . $prod . "<br />";
Results:
536870912 << 5 == 0
536870912 >> 5 == 16777216
536870912 >> 37 == 16777216
4 >> 5 == 0
4 << 5 == 128
4 << 37 == 128
| |