Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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