Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Exception;
   7  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   8  use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
   9  
  10  class BitWise
  11  {
  12      use ArrayEnabled;
  13  
  14      const SPLIT_DIVISOR = 2 ** 24;
  15  
  16      /**
  17       * Split a number into upper and lower portions for full 32-bit support.
  18       *
  19       * @param float|int $number
  20       */
  21      private static function splitNumber($number): array
  22      {
  23          return [floor($number / self::SPLIT_DIVISOR), fmod($number, self::SPLIT_DIVISOR)];
  24      }
  25  
  26      /**
  27       * BITAND.
  28       *
  29       * Returns the bitwise AND of two integer values.
  30       *
  31       * Excel Function:
  32       *        BITAND(number1, number2)
  33       *
  34       * @param array|int $number1
  35       *                      Or can be an array of values
  36       * @param array|int $number2
  37       *                      Or can be an array of values
  38       *
  39       * @return array|int|string
  40       *         If an array of numbers is passed as an argument, then the returned result will also be an array
  41       *            with the same dimensions
  42       */
  43      public static function BITAND($number1, $number2)
  44      {
  45          if (is_array($number1) || is_array($number2)) {
  46              return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
  47          }
  48  
  49          try {
  50              $number1 = self::validateBitwiseArgument($number1);
  51              $number2 = self::validateBitwiseArgument($number2);
  52          } catch (Exception $e) {
  53              return $e->getMessage();
  54          }
  55          $split1 = self::splitNumber($number1);
  56          $split2 = self::splitNumber($number2);
  57  
  58          return  self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
  59      }
  60  
  61      /**
  62       * BITOR.
  63       *
  64       * Returns the bitwise OR of two integer values.
  65       *
  66       * Excel Function:
  67       *        BITOR(number1, number2)
  68       *
  69       * @param array|int $number1
  70       *                      Or can be an array of values
  71       * @param array|int $number2
  72       *                      Or can be an array of values
  73       *
  74       * @return array|int|string
  75       *         If an array of numbers is passed as an argument, then the returned result will also be an array
  76       *            with the same dimensions
  77       */
  78      public static function BITOR($number1, $number2)
  79      {
  80          if (is_array($number1) || is_array($number2)) {
  81              return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
  82          }
  83  
  84          try {
  85              $number1 = self::validateBitwiseArgument($number1);
  86              $number2 = self::validateBitwiseArgument($number2);
  87          } catch (Exception $e) {
  88              return $e->getMessage();
  89          }
  90  
  91          $split1 = self::splitNumber($number1);
  92          $split2 = self::splitNumber($number2);
  93  
  94          return  self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
  95      }
  96  
  97      /**
  98       * BITXOR.
  99       *
 100       * Returns the bitwise XOR of two integer values.
 101       *
 102       * Excel Function:
 103       *        BITXOR(number1, number2)
 104       *
 105       * @param array|int $number1
 106       *                      Or can be an array of values
 107       * @param array|int $number2
 108       *                      Or can be an array of values
 109       *
 110       * @return array|int|string
 111       *         If an array of numbers is passed as an argument, then the returned result will also be an array
 112       *            with the same dimensions
 113       */
 114      public static function BITXOR($number1, $number2)
 115      {
 116          if (is_array($number1) || is_array($number2)) {
 117              return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
 118          }
 119  
 120          try {
 121              $number1 = self::validateBitwiseArgument($number1);
 122              $number2 = self::validateBitwiseArgument($number2);
 123          } catch (Exception $e) {
 124              return $e->getMessage();
 125          }
 126  
 127          $split1 = self::splitNumber($number1);
 128          $split2 = self::splitNumber($number2);
 129  
 130          return  self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
 131      }
 132  
 133      /**
 134       * BITLSHIFT.
 135       *
 136       * Returns the number value shifted left by shift_amount bits.
 137       *
 138       * Excel Function:
 139       *        BITLSHIFT(number, shift_amount)
 140       *
 141       * @param array|int $number
 142       *                      Or can be an array of values
 143       * @param array|int $shiftAmount
 144       *                      Or can be an array of values
 145       *
 146       * @return array|float|int|string
 147       *         If an array of numbers is passed as an argument, then the returned result will also be an array
 148       *            with the same dimensions
 149       */
 150      public static function BITLSHIFT($number, $shiftAmount)
 151      {
 152          if (is_array($number) || is_array($shiftAmount)) {
 153              return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
 154          }
 155  
 156          try {
 157              $number = self::validateBitwiseArgument($number);
 158              $shiftAmount = self::validateShiftAmount($shiftAmount);
 159          } catch (Exception $e) {
 160              return $e->getMessage();
 161          }
 162  
 163          $result = floor($number * (2 ** $shiftAmount));
 164          if ($result > 2 ** 48 - 1) {
 165              return ExcelError::NAN();
 166          }
 167  
 168          return $result;
 169      }
 170  
 171      /**
 172       * BITRSHIFT.
 173       *
 174       * Returns the number value shifted right by shift_amount bits.
 175       *
 176       * Excel Function:
 177       *        BITRSHIFT(number, shift_amount)
 178       *
 179       * @param array|int $number
 180       *                      Or can be an array of values
 181       * @param array|int $shiftAmount
 182       *                      Or can be an array of values
 183       *
 184       * @return array|float|int|string
 185       *         If an array of numbers is passed as an argument, then the returned result will also be an array
 186       *            with the same dimensions
 187       */
 188      public static function BITRSHIFT($number, $shiftAmount)
 189      {
 190          if (is_array($number) || is_array($shiftAmount)) {
 191              return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
 192          }
 193  
 194          try {
 195              $number = self::validateBitwiseArgument($number);
 196              $shiftAmount = self::validateShiftAmount($shiftAmount);
 197          } catch (Exception $e) {
 198              return $e->getMessage();
 199          }
 200  
 201          $result = floor($number / (2 ** $shiftAmount));
 202          if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
 203              return ExcelError::NAN();
 204          }
 205  
 206          return $result;
 207      }
 208  
 209      /**
 210       * Validate arguments passed to the bitwise functions.
 211       *
 212       * @param mixed $value
 213       *
 214       * @return float
 215       */
 216      private static function validateBitwiseArgument($value)
 217      {
 218          $value = self::nullFalseTrueToNumber($value);
 219  
 220          if (is_numeric($value)) {
 221              $value = (float) $value;
 222              if ($value == floor($value)) {
 223                  if (($value > 2 ** 48 - 1) || ($value < 0)) {
 224                      throw new Exception(ExcelError::NAN());
 225                  }
 226  
 227                  return floor($value);
 228              }
 229  
 230              throw new Exception(ExcelError::NAN());
 231          }
 232  
 233          throw new Exception(ExcelError::VALUE());
 234      }
 235  
 236      /**
 237       * Validate arguments passed to the bitwise functions.
 238       *
 239       * @param mixed $value
 240       *
 241       * @return int
 242       */
 243      private static function validateShiftAmount($value)
 244      {
 245          $value = self::nullFalseTrueToNumber($value);
 246  
 247          if (is_numeric($value)) {
 248              if (abs($value) > 53) {
 249                  throw new Exception(ExcelError::NAN());
 250              }
 251  
 252              return (int) $value;
 253          }
 254  
 255          throw new Exception(ExcelError::VALUE());
 256      }
 257  
 258      /**
 259       * Many functions accept null/false/true argument treated as 0/0/1.
 260       *
 261       * @param mixed $number
 262       *
 263       * @return mixed
 264       */
 265      private static function nullFalseTrueToNumber(&$number)
 266      {
 267          if ($number === null) {
 268              $number = 0;
 269          } elseif (is_bool($number)) {
 270              $number = (int) $number;
 271          }
 272  
 273          return $number;
 274      }
 275  }