PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store
Related Articles

PHP A to ZCE: Bitwise Operations

This article is part of the series “PHP A to Zend Certified Engineer”. In PHP A to ZCE, I will take you through 26 different yet equally important topics that will help you become a Zend Certified Engineer. Even if you're not interested in sitting the ZCE-PHP exam, these topics will elevate your understanding of PHP to a whole new level and allow you to become the guru in your company. Read more about PHP A to Zend Certified Engineer...

Bitwise operations allow evaluation and manipulation of specific bits within an integer. In this article I will show each of the bitwise operations that are available and how to practically use them within PHP.

About Bitwise Operations

  • Primarily used as a series of on/off "switches".
  • Each switch corresponds to a single bit in an integer
  • Bitwise operators allow you to turn on or turn off various switches
  • Easiest to think of terms of binary numbers and operate with hex values
  • Bits are read from right-to-left

As an example, the following shows breaking down the number 13 into binary digits. Think of this number as a series of switches, the first, third and fourth "switches" are enabled (reading from the right).

Listing 9 listing-9.txt
Position: 128 64 32 16  8  4  2  1
----------------------------------
Number:     0  0  0  0  1  1  0  1

Available Operators

There are six bitwise operations available. All operations use two values (that is, they have a left and a right operand) except for "not", which is a unary operator (has a single operand only).

The operations and their result are as follows:

  • And ($c = $a & $b;): Bits that are set in both $a and $b are set in $c.
  • Or ($c = $a | $b): Bits that are set in either $a and $b are set in $c.
  • Exclusive Or (Xor) ($c = $a ^ $b): Bits that are set in one and not the other are set in $c.
  • Not ($c = ~$a): Bits that are set in $a are not set in $c. Bits that are not set in $a are set in $c.
  • Shift Left ($c = $a << $b): $b indicates the number of positions the bits in $a are to move left. Each position move in effect is the same as multiplying by 2.
  • Shift Right ($c = $a >> $b): $b indicates the number of positions the bits in $a are to move right. Each position move in effect is the same as dividing by 2 (the remainder is ignore since we're operating on integers).

PHP also supports the following syntax:

  • $a = $a & $b; can be shortened to $a &= $b;
  • $a = $a | $b; can be shortened to $a |= $b;
  • $a = $a ^ $b; can be shortened to $a ^= $b;

Hexadecimal Numbers

  • Every four bits can be represented by a single hex digit
  • Hex is 0-9, A=10, B=11, C=12, D=13, E=14, F=15
  • The number 13 can be represented by D in hex
  • All hex numbers are preceded by 0x, meaning 13 is actually 0xD.
  • All switches on: 0xF - all switches off: 0x0
  • You can output hex numbers using the %X switch in sprintf()

The And Operation

Bits that are set in both $a and $b are set in $c.

Listing 1 listing-1.php
<?php
    $a = 0xD; // 13 in decimal, 1101 in binary
    $b = 0x7; // 7 in decimal 0111 in binary
 
    $c = $a & $b;
 
    /*
          1101  (13, 0xD)
        & 0111  (7, 0x7)
          ----
          0101  (5, 0x5)
    */
 
    echo $c; // outputs 5
?>

The Or Operation

Bits that are set in either $a and $b are set in $c.

Listing 2 listing-2.php
<?php
    $a = 0xD; // 13 in decimal, 1101 in binary
    $b = 0x7; // 7 in decimal 0111 in binary
 
    $c = $a | $b;
 
    /*
          1101  (13, 0xD)
        | 0111  (7, 0x7)
          ----
          1111  (15, 0xF)
    */
 
    echo $c; // outputs 15
?>

The Xor (Exclusive Or) Operation

Bits that are set in one and not the other are set in $c.

Listing 3 listing-3.php
<?php
    $a = 0xD; // 13 in decimal, 1101 in binary
    $b = 0x7; // 7 in decimal 0111 in binary
 
    $c = $a | $b;
 
    /*
          1101  (13, 0xD)
        ^ 0111  (7, 0x7)
          ----
          1010  (10, 0xA)
    */
 
    echo $c; // outputs 10
?>

The Not Operation

Bits that are set in $a are not set in $c. Bits that are not set in $a are set in $c.

Listing 4 listing-4.php
<?php
    $a = 0xD; // 13 in decimal, 1101 in binary
 
    $c = ~$a
 
    /*
        ~ 1101  (13, 0xD)
          ----
          0010  (2, 0x2)
    */
 
    echo $c; // outputs 2
?>

The Shift Left Operation

$b indicates the number of positions the bits in $a are to move left. Each position move in effect is the same as multiplying by 2.

Listing 5 listing-5.php
<?php
    $a = 0xD; // 13 in decimal, 1101 in binary
    $b = 2;
 
    $c = $a << $b;
 
    /*
          000001101  (13, 0xD)
          ---------
             << 2
          ---------
          000110100  (52, 0x34)
    */
 
    echo $c; // outputs 52
    echo $a * 2 * 2; // outputs 52
?>

The Shift Right Operation

$b indicates the number of positions the bits in $a are to move right. Each position move in effect is the same as dividing by 2 (the remainder is ignore since we're operating on integers).

Listing 6 listing-6.php
<?php
    $a = 0xD; // 13 in decimal, 1101 in binary
    $b = 1;
 
    $c = $a >> $b;
 
    /*
          000001101  (13, 0xD)
          ---------
             >> 1
          ---------
          000000110  (6, 0x6)
    */
 
    echo $c; // outputs 6
    echo floor($a / 2); // outputs 6
    // floor is used because the output must be an int
?>

Practical Example 1: PHP Error Reporting

  • PHP's error reporting system is an example of using bitwise operations and switches
  • You can change the reporting level by turning switches on or off and passing the result to error_reporting()
Listing 10 listing-10.txt
E_ERROR   = 1 = 0001 = 0x1
E_WARNING = 2 = 0010 = 0x2
E_PARSE   = 4 = 0100 = 0x4
E_NOTICE  = 8 = 1000 = 0x8
  • To enable errors and warnings, use E_ERROR | E_WARNING
Listing 11 listing-11.txt
E_ERROR   = 0001
E_WARNING = 0010
            ----
            0011
  • You can check if a particular level is enabled using "and"
  • If that switch is not enabled the result will be zero (0x0)
Listing 7 listing-7.php
<?php
    error_reporting(E_ERROR | E_WARNING);
 
    if (error_reporting() & E_WARNING) {
        // E_WARNING is enabled
    }
 
    /*
        error_reporting(): 0011
        E_WARNING:         0010
                           ----
                         & 0010 - Therefore E_WARNING is enabled!
     */
?>

Practical Example 2: IP Addresses and Subnet Masks

  • You can use bitwise operations to see if an IP address is within a given subnet
  • You need the subnet and the subnet mask to determine this
  • You also need to convert the IP address to an integer representation so bitwise operations will work: ip2long() and long2ip() help with this.
Listing 8 listing-8.php
<?php
    $ips = array(
        '192.168.0.25',
        '10.0.0.1'
    );
 
    $subnet     = ip2long('192.168.0.0');
    $subnetMask = ip2long('255.255.255.0');
 
    foreach ($ips as $ip) {
        $longIp = ip2long($ip);
 
        if (($longIp & $subnetMask) == $subnet) {
            echo sprintf("%s is in %s subnet\n", $ip, long2ip($subnet));
        }
        else {
            echo sprintf("%s is not in %s subnet\n", $ip, long2ip($subnet));
        }
    }
 
    /*
        Output:
        192.168.0.25 is in 192.168.0.0 subnet
        10.0.0.1 is not in 192.168.0.0 subnet
    */
?>
Note: Be sure to use brackets appropriately. If you don't use brackets in the above if statement, the == comparison is performed before the bitwise operation.

Further Reading

Other Options

PHP A to ZCE: Bitwise Operations