[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/sodium_compat/src/Core/ -> Util.php (source)

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Core_Util', false)) {
   4      return;
   5  }
   6  
   7  /**
   8   * Class ParagonIE_Sodium_Core_Util
   9   */
  10  abstract class ParagonIE_Sodium_Core_Util
  11  {
  12      const U32_MAX = 0xFFFFFFFF;
  13  
  14      /**
  15       * @param int $integer
  16       * @param int $size (16, 32, 64)
  17       * @return int
  18       */
  19      public static function abs($integer, $size = 0)
  20      {
  21          /** @var int $realSize */
  22          $realSize = (PHP_INT_SIZE << 3) - 1;
  23          if ($size) {
  24              --$size;
  25          } else {
  26              /** @var int $size */
  27              $size = $realSize;
  28          }
  29  
  30          $negative = -(($integer >> $size) & 1);
  31          return (int) (
  32              ($integer ^ $negative)
  33                  +
  34              (($negative >> $realSize) & 1)
  35          );
  36      }
  37  
  38      /**
  39       * @param string $a
  40       * @param string $b
  41       * @return string
  42       * @throws SodiumException
  43       */
  44      public static function andStrings($a, $b)
  45      {
  46          /* Type checks: */
  47          if (!is_string($a)) {
  48              throw new TypeError('Argument 1 must be a string');
  49          }
  50          if (!is_string($b)) {
  51              throw new TypeError('Argument 2 must be a string');
  52          }
  53          $len = self::strlen($a);
  54          if (self::strlen($b) !== $len) {
  55              throw new SodiumException('Both strings must be of equal length to combine with bitwise AND');
  56          }
  57          return $a & $b;
  58      }
  59  
  60      /**
  61       * Convert a binary string into a hexadecimal string without cache-timing
  62       * leaks
  63       *
  64       * @internal You should not use this directly from another application
  65       *
  66       * @param string $binaryString (raw binary)
  67       * @return string
  68       * @throws TypeError
  69       */
  70      public static function bin2hex($binaryString)
  71      {
  72          /* Type checks: */
  73          if (!is_string($binaryString)) {
  74              throw new TypeError('Argument 1 must be a string, ' . gettype($binaryString) . ' given.');
  75          }
  76  
  77          $hex = '';
  78          $len = self::strlen($binaryString);
  79          for ($i = 0; $i < $len; ++$i) {
  80              /** @var array<int, int> $chunk */
  81              $chunk = unpack('C', $binaryString[$i]);
  82              /** @var int $c */
  83              $c = $chunk[1] & 0xf;
  84              /** @var int $b */
  85              $b = $chunk[1] >> 4;
  86              $hex .= pack(
  87                  'CC',
  88                  (87 + $b + ((($b - 10) >> 8) & ~38)),
  89                  (87 + $c + ((($c - 10) >> 8) & ~38))
  90              );
  91          }
  92          return $hex;
  93      }
  94  
  95      /**
  96       * Convert a binary string into a hexadecimal string without cache-timing
  97       * leaks, returning uppercase letters (as per RFC 4648)
  98       *
  99       * @internal You should not use this directly from another application
 100       *
 101       * @param string $bin_string (raw binary)
 102       * @return string
 103       * @throws TypeError
 104       */
 105      public static function bin2hexUpper($bin_string)
 106      {
 107          $hex = '';
 108          $len = self::strlen($bin_string);
 109          for ($i = 0; $i < $len; ++$i) {
 110              /** @var array<int, int> $chunk */
 111              $chunk = unpack('C', $bin_string[$i]);
 112              /**
 113               * Lower 16 bits
 114               *
 115               * @var int $c
 116               */
 117              $c = $chunk[1] & 0xf;
 118  
 119              /**
 120               * Upper 16 bits
 121               * @var int $b
 122               */
 123              $b = $chunk[1] >> 4;
 124  
 125              /**
 126               * Use pack() and binary operators to turn the two integers
 127               * into hexadecimal characters. We don't use chr() here, because
 128               * it uses a lookup table internally and we want to avoid
 129               * cache-timing side-channels.
 130               */
 131              $hex .= pack(
 132                  'CC',
 133                  (55 + $b + ((($b - 10) >> 8) & ~6)),
 134                  (55 + $c + ((($c - 10) >> 8) & ~6))
 135              );
 136          }
 137          return $hex;
 138      }
 139  
 140      /**
 141       * Cache-timing-safe variant of ord()
 142       *
 143       * @internal You should not use this directly from another application
 144       *
 145       * @param string $chr
 146       * @return int
 147       * @throws SodiumException
 148       * @throws TypeError
 149       */
 150      public static function chrToInt($chr)
 151      {
 152          /* Type checks: */
 153          if (!is_string($chr)) {
 154              throw new TypeError('Argument 1 must be a string, ' . gettype($chr) . ' given.');
 155          }
 156          if (self::strlen($chr) !== 1) {
 157              throw new SodiumException('chrToInt() expects a string that is exactly 1 character long');
 158          }
 159          /** @var array<int, int> $chunk */
 160          $chunk = unpack('C', $chr);
 161          return (int) ($chunk[1]);
 162      }
 163  
 164      /**
 165       * Compares two strings.
 166       *
 167       * @internal You should not use this directly from another application
 168       *
 169       * @param string $left
 170       * @param string $right
 171       * @param int $len
 172       * @return int
 173       * @throws SodiumException
 174       * @throws TypeError
 175       */
 176      public static function compare($left, $right, $len = null)
 177      {
 178          $leftLen = self::strlen($left);
 179          $rightLen = self::strlen($right);
 180          if ($len === null) {
 181              $len = max($leftLen, $rightLen);
 182              $left = str_pad($left, $len, "\x00", STR_PAD_RIGHT);
 183              $right = str_pad($right, $len, "\x00", STR_PAD_RIGHT);
 184          }
 185  
 186          $gt = 0;
 187          $eq = 1;
 188          $i = $len;
 189          while ($i !== 0) {
 190              --$i;
 191              $gt |= ((self::chrToInt($right[$i]) - self::chrToInt($left[$i])) >> 8) & $eq;
 192              $eq &= ((self::chrToInt($right[$i]) ^ self::chrToInt($left[$i])) - 1) >> 8;
 193          }
 194          return ($gt + $gt + $eq) - 1;
 195      }
 196  
 197      /**
 198       * If a variable does not match a given type, throw a TypeError.
 199       *
 200       * @param mixed $mixedVar
 201       * @param string $type
 202       * @param int $argumentIndex
 203       * @throws TypeError
 204       * @throws SodiumException
 205       * @return void
 206       */
 207      public static function declareScalarType(&$mixedVar = null, $type = 'void', $argumentIndex = 0)
 208      {
 209          if (func_num_args() === 0) {
 210              /* Tautology, by default */
 211              return;
 212          }
 213          if (func_num_args() === 1) {
 214              throw new TypeError('Declared void, but passed a variable');
 215          }
 216          $realType = strtolower(gettype($mixedVar));
 217          $type = strtolower($type);
 218          switch ($type) {
 219              case 'null':
 220                  if ($mixedVar !== null) {
 221                      throw new TypeError('Argument ' . $argumentIndex . ' must be null, ' . $realType . ' given.');
 222                  }
 223                  break;
 224              case 'integer':
 225              case 'int':
 226                  $allow = array('int', 'integer');
 227                  if (!in_array($type, $allow)) {
 228                      throw new TypeError('Argument ' . $argumentIndex . ' must be an integer, ' . $realType . ' given.');
 229                  }
 230                  $mixedVar = (int) $mixedVar;
 231                  break;
 232              case 'boolean':
 233              case 'bool':
 234                  $allow = array('bool', 'boolean');
 235                  if (!in_array($type, $allow)) {
 236                      throw new TypeError('Argument ' . $argumentIndex . ' must be a boolean, ' . $realType . ' given.');
 237                  }
 238                  $mixedVar = (bool) $mixedVar;
 239                  break;
 240              case 'string':
 241                  if (!is_string($mixedVar)) {
 242                      throw new TypeError('Argument ' . $argumentIndex . ' must be a string, ' . $realType . ' given.');
 243                  }
 244                  $mixedVar = (string) $mixedVar;
 245                  break;
 246              case 'decimal':
 247              case 'double':
 248              case 'float':
 249                  $allow = array('decimal', 'double', 'float');
 250                  if (!in_array($type, $allow)) {
 251                      throw new TypeError('Argument ' . $argumentIndex . ' must be a float, ' . $realType . ' given.');
 252                  }
 253                  $mixedVar = (float) $mixedVar;
 254                  break;
 255              case 'object':
 256                  if (!is_object($mixedVar)) {
 257                      throw new TypeError('Argument ' . $argumentIndex . ' must be an object, ' . $realType . ' given.');
 258                  }
 259                  break;
 260              case 'array':
 261                  if (!is_array($mixedVar)) {
 262                      if (is_object($mixedVar)) {
 263                          if ($mixedVar instanceof ArrayAccess) {
 264                              return;
 265                          }
 266                      }
 267                      throw new TypeError('Argument ' . $argumentIndex . ' must be an array, ' . $realType . ' given.');
 268                  }
 269                  break;
 270              default:
 271                  throw new SodiumException('Unknown type (' . $realType .') does not match expect type (' . $type . ')');
 272          }
 273      }
 274  
 275      /**
 276       * Evaluate whether or not two strings are equal (in constant-time)
 277       *
 278       * @param string $left
 279       * @param string $right
 280       * @return bool
 281       * @throws SodiumException
 282       * @throws TypeError
 283       */
 284      public static function hashEquals($left, $right)
 285      {
 286          /* Type checks: */
 287          if (!is_string($left)) {
 288              throw new TypeError('Argument 1 must be a string, ' . gettype($left) . ' given.');
 289          }
 290          if (!is_string($right)) {
 291              throw new TypeError('Argument 2 must be a string, ' . gettype($right) . ' given.');
 292          }
 293  
 294          if (is_callable('hash_equals')) {
 295              return hash_equals($left, $right);
 296          }
 297          $d = 0;
 298          /** @var int $len */
 299          $len = self::strlen($left);
 300          if ($len !== self::strlen($right)) {
 301              return false;
 302          }
 303          for ($i = 0; $i < $len; ++$i) {
 304              $d |= self::chrToInt($left[$i]) ^ self::chrToInt($right[$i]);
 305          }
 306  
 307          if ($d !== 0) {
 308              return false;
 309          }
 310          return $left === $right;
 311      }
 312  
 313      /**
 314       * Catch hash_update() failures and throw instead of silently proceeding
 315       *
 316       * @param HashContext|resource &$hs
 317       * @param string $data
 318       * @return void
 319       * @throws SodiumException
 320       * @psalm-suppress PossiblyInvalidArgument
 321       */
 322      protected static function hash_update(&$hs, $data)
 323      {
 324          if (!hash_update($hs, $data)) {
 325              throw new SodiumException('hash_update() failed');
 326          }
 327      }
 328  
 329      /**
 330       * Convert a hexadecimal string into a binary string without cache-timing
 331       * leaks
 332       *
 333       * @internal You should not use this directly from another application
 334       *
 335       * @param string $hexString
 336       * @param string $ignore
 337       * @param bool $strictPadding
 338       * @return string (raw binary)
 339       * @throws RangeException
 340       * @throws TypeError
 341       */
 342      public static function hex2bin($hexString, $ignore = '', $strictPadding = false)
 343      {
 344          /* Type checks: */
 345          if (!is_string($hexString)) {
 346              throw new TypeError('Argument 1 must be a string, ' . gettype($hexString) . ' given.');
 347          }
 348          if (!is_string($ignore)) {
 349              throw new TypeError('Argument 2 must be a string, ' . gettype($hexString) . ' given.');
 350          }
 351  
 352          $hex_pos = 0;
 353          $bin = '';
 354          $c_acc = 0;
 355          $hex_len = self::strlen($hexString);
 356          $state = 0;
 357          if (($hex_len & 1) !== 0) {
 358              if ($strictPadding) {
 359                  throw new RangeException(
 360                      'Expected an even number of hexadecimal characters'
 361                  );
 362              } else {
 363                  $hexString = '0' . $hexString;
 364                  ++$hex_len;
 365              }
 366          }
 367  
 368          $chunk = unpack('C*', $hexString);
 369          while ($hex_pos < $hex_len) {
 370              ++$hex_pos;
 371              /** @var int $c */
 372              $c = $chunk[$hex_pos];
 373              $c_num = $c ^ 48;
 374              $c_num0 = ($c_num - 10) >> 8;
 375              $c_alpha = ($c & ~32) - 55;
 376              $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
 377              if (($c_num0 | $c_alpha0) === 0) {
 378                  if ($ignore && $state === 0 && strpos($ignore, self::intToChr($c)) !== false) {
 379                      continue;
 380                  }
 381                  throw new RangeException(
 382                      'hex2bin() only expects hexadecimal characters'
 383                  );
 384              }
 385              $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
 386              if ($state === 0) {
 387                  $c_acc = $c_val * 16;
 388              } else {
 389                  $bin .= pack('C', $c_acc | $c_val);
 390              }
 391              $state ^= 1;
 392          }
 393          return $bin;
 394      }
 395  
 396      /**
 397       * Turn an array of integers into a string
 398       *
 399       * @internal You should not use this directly from another application
 400       *
 401       * @param array<int, int> $ints
 402       * @return string
 403       */
 404      public static function intArrayToString(array $ints)
 405      {
 406          $args = $ints;
 407          foreach ($args as $i => $v) {
 408              $args[$i] = (int) ($v & 0xff);
 409          }
 410          array_unshift($args, str_repeat('C', count($ints)));
 411          return (string) (call_user_func_array('pack', $args));
 412      }
 413  
 414      /**
 415       * Cache-timing-safe variant of ord()
 416       *
 417       * @internal You should not use this directly from another application
 418       *
 419       * @param int $int
 420       * @return string
 421       * @throws TypeError
 422       */
 423      public static function intToChr($int)
 424      {
 425          return pack('C', $int);
 426      }
 427  
 428      /**
 429       * Load a 3 character substring into an integer
 430       *
 431       * @internal You should not use this directly from another application
 432       *
 433       * @param string $string
 434       * @return int
 435       * @throws RangeException
 436       * @throws TypeError
 437       */
 438      public static function load_3($string)
 439      {
 440          /* Type checks: */
 441          if (!is_string($string)) {
 442              throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
 443          }
 444  
 445          /* Input validation: */
 446          if (self::strlen($string) < 3) {
 447              throw new RangeException(
 448                  'String must be 3 bytes or more; ' . self::strlen($string) . ' given.'
 449              );
 450          }
 451          /** @var array<int, int> $unpacked */
 452          $unpacked = unpack('V', $string . "\0");
 453          return (int) ($unpacked[1] & 0xffffff);
 454      }
 455  
 456      /**
 457       * Load a 4 character substring into an integer
 458       *
 459       * @internal You should not use this directly from another application
 460       *
 461       * @param string $string
 462       * @return int
 463       * @throws RangeException
 464       * @throws TypeError
 465       */
 466      public static function load_4($string)
 467      {
 468          /* Type checks: */
 469          if (!is_string($string)) {
 470              throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
 471          }
 472  
 473          /* Input validation: */
 474          if (self::strlen($string) < 4) {
 475              throw new RangeException(
 476                  'String must be 4 bytes or more; ' . self::strlen($string) . ' given.'
 477              );
 478          }
 479          /** @var array<int, int> $unpacked */
 480          $unpacked = unpack('V', $string);
 481          return (int) $unpacked[1];
 482      }
 483  
 484      /**
 485       * Load a 8 character substring into an integer
 486       *
 487       * @internal You should not use this directly from another application
 488       *
 489       * @param string $string
 490       * @return int
 491       * @throws RangeException
 492       * @throws SodiumException
 493       * @throws TypeError
 494       */
 495      public static function load64_le($string)
 496      {
 497          /* Type checks: */
 498          if (!is_string($string)) {
 499              throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.');
 500          }
 501  
 502          /* Input validation: */
 503          if (self::strlen($string) < 4) {
 504              throw new RangeException(
 505                  'String must be 4 bytes or more; ' . self::strlen($string) . ' given.'
 506              );
 507          }
 508          if (PHP_VERSION_ID >= 50603 && PHP_INT_SIZE === 8) {
 509              /** @var array<int, int> $unpacked */
 510              $unpacked = unpack('P', $string);
 511              return (int) $unpacked[1];
 512          }
 513  
 514          /** @var int $result */
 515          $result  = (self::chrToInt($string[0]) & 0xff);
 516          $result |= (self::chrToInt($string[1]) & 0xff) <<  8;
 517          $result |= (self::chrToInt($string[2]) & 0xff) << 16;
 518          $result |= (self::chrToInt($string[3]) & 0xff) << 24;
 519          $result |= (self::chrToInt($string[4]) & 0xff) << 32;
 520          $result |= (self::chrToInt($string[5]) & 0xff) << 40;
 521          $result |= (self::chrToInt($string[6]) & 0xff) << 48;
 522          $result |= (self::chrToInt($string[7]) & 0xff) << 56;
 523          return (int) $result;
 524      }
 525  
 526      /**
 527       * @internal You should not use this directly from another application
 528       *
 529       * @param string $left
 530       * @param string $right
 531       * @return int
 532       * @throws SodiumException
 533       * @throws TypeError
 534       */
 535      public static function memcmp($left, $right)
 536      {
 537          if (self::hashEquals($left, $right)) {
 538              return 0;
 539          }
 540          return -1;
 541      }
 542  
 543      /**
 544       * Multiply two integers in constant-time
 545       *
 546       * Micro-architecture timing side-channels caused by how your CPU
 547       * implements multiplication are best prevented by never using the
 548       * multiplication operators and ensuring that our code always takes
 549       * the same number of operations to complete, regardless of the values
 550       * of $a and $b.
 551       *
 552       * @internal You should not use this directly from another application
 553       *
 554       * @param int $a
 555       * @param int $b
 556       * @param int $size Limits the number of operations (useful for small,
 557       *                  constant operands)
 558       * @return int
 559       */
 560      public static function mul($a, $b, $size = 0)
 561      {
 562          if (ParagonIE_Sodium_Compat::$fastMult) {
 563              return (int) ($a * $b);
 564          }
 565  
 566          static $defaultSize = null;
 567          /** @var int $defaultSize */
 568          if (!$defaultSize) {
 569              /** @var int $defaultSize */
 570              $defaultSize = (PHP_INT_SIZE << 3) - 1;
 571          }
 572          if ($size < 1) {
 573              /** @var int $size */
 574              $size = $defaultSize;
 575          }
 576          /** @var int $size */
 577  
 578          $c = 0;
 579  
 580          /**
 581           * Mask is either -1 or 0.
 582           *
 583           * -1 in binary looks like 0x1111 ... 1111
 584           *  0 in binary looks like 0x0000 ... 0000
 585           *
 586           * @var int
 587           */
 588          $mask = -(($b >> ((int) $defaultSize)) & 1);
 589  
 590          /**
 591           * Ensure $b is a positive integer, without creating
 592           * a branching side-channel
 593           *
 594           * @var int $b
 595           */
 596          $b = ($b & ~$mask) | ($mask & -$b);
 597  
 598          /**
 599           * Unless $size is provided:
 600           *
 601           * This loop always runs 32 times when PHP_INT_SIZE is 4.
 602           * This loop always runs 64 times when PHP_INT_SIZE is 8.
 603           */
 604          for ($i = $size; $i >= 0; --$i) {
 605              $c += (int) ($a & -($b & 1));
 606              $a <<= 1;
 607              $b >>= 1;
 608          }
 609          $c = (int) @($c & -1);
 610  
 611          /**
 612           * If $b was negative, we then apply the same value to $c here.
 613           * It doesn't matter much if $a was negative; the $c += above would
 614           * have produced a negative integer to begin with. But a negative $b
 615           * makes $b >>= 1 never return 0, so we would end up with incorrect
 616           * results.
 617           *
 618           * The end result is what we'd expect from integer multiplication.
 619           */
 620          return (int) (($c & ~$mask) | ($mask & -$c));
 621      }
 622  
 623      /**
 624       * Convert any arbitrary numbers into two 32-bit integers that represent
 625       * a 64-bit integer.
 626       *
 627       * @internal You should not use this directly from another application
 628       *
 629       * @param int|float $num
 630       * @return array<int, int>
 631       */
 632      public static function numericTo64BitInteger($num)
 633      {
 634          $high = 0;
 635          /** @var int $low */
 636          if (PHP_INT_SIZE === 4) {
 637              $low = (int) $num;
 638          } else {
 639              $low = $num & 0xffffffff;
 640          }
 641  
 642          if ((+(abs($num))) >= 1) {
 643              if ($num > 0) {
 644                  /** @var int $high */
 645                  $high = min((+(floor($num/4294967296))), 4294967295);
 646              } else {
 647                  /** @var int $high */
 648                  $high = ~~((+(ceil(($num - (+((~~($num)))))/4294967296))));
 649              }
 650          }
 651          return array((int) $high, (int) $low);
 652      }
 653  
 654      /**
 655       * Store a 24-bit integer into a string, treating it as big-endian.
 656       *
 657       * @internal You should not use this directly from another application
 658       *
 659       * @param int $int
 660       * @return string
 661       * @throws TypeError
 662       */
 663      public static function store_3($int)
 664      {
 665          /* Type checks: */
 666          if (!is_int($int)) {
 667              if (is_numeric($int)) {
 668                  $int = (int) $int;
 669              } else {
 670                  throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
 671              }
 672          }
 673          /** @var string $packed */
 674          $packed = pack('N', $int);
 675          return self::substr($packed, 1, 3);
 676      }
 677  
 678      /**
 679       * Store a 32-bit integer into a string, treating it as little-endian.
 680       *
 681       * @internal You should not use this directly from another application
 682       *
 683       * @param int $int
 684       * @return string
 685       * @throws TypeError
 686       */
 687      public static function store32_le($int)
 688      {
 689          /* Type checks: */
 690          if (!is_int($int)) {
 691              if (is_numeric($int)) {
 692                  $int = (int) $int;
 693              } else {
 694                  throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
 695              }
 696          }
 697  
 698          /** @var string $packed */
 699          $packed = pack('V', $int);
 700          return $packed;
 701      }
 702  
 703      /**
 704       * Store a 32-bit integer into a string, treating it as big-endian.
 705       *
 706       * @internal You should not use this directly from another application
 707       *
 708       * @param int $int
 709       * @return string
 710       * @throws TypeError
 711       */
 712      public static function store_4($int)
 713      {
 714          /* Type checks: */
 715          if (!is_int($int)) {
 716              if (is_numeric($int)) {
 717                  $int = (int) $int;
 718              } else {
 719                  throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
 720              }
 721          }
 722  
 723          /** @var string $packed */
 724          $packed = pack('N', $int);
 725          return $packed;
 726      }
 727  
 728      /**
 729       * Stores a 64-bit integer as an string, treating it as little-endian.
 730       *
 731       * @internal You should not use this directly from another application
 732       *
 733       * @param int $int
 734       * @return string
 735       * @throws TypeError
 736       */
 737      public static function store64_le($int)
 738      {
 739          /* Type checks: */
 740          if (!is_int($int)) {
 741              if (is_numeric($int)) {
 742                  $int = (int) $int;
 743              } else {
 744                  throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.');
 745              }
 746          }
 747  
 748          if (PHP_INT_SIZE === 8) {
 749              if (PHP_VERSION_ID >= 50603) {
 750                  /** @var string $packed */
 751                  $packed = pack('P', $int);
 752                  return $packed;
 753              }
 754              return self::intToChr($int & 0xff) .
 755                  self::intToChr(($int >>  8) & 0xff) .
 756                  self::intToChr(($int >> 16) & 0xff) .
 757                  self::intToChr(($int >> 24) & 0xff) .
 758                  self::intToChr(($int >> 32) & 0xff) .
 759                  self::intToChr(($int >> 40) & 0xff) .
 760                  self::intToChr(($int >> 48) & 0xff) .
 761                  self::intToChr(($int >> 56) & 0xff);
 762          }
 763          if ($int > PHP_INT_MAX) {
 764              list($hiB, $int) = self::numericTo64BitInteger($int);
 765          } else {
 766              $hiB = 0;
 767          }
 768          return
 769              self::intToChr(($int      ) & 0xff) .
 770              self::intToChr(($int >>  8) & 0xff) .
 771              self::intToChr(($int >> 16) & 0xff) .
 772              self::intToChr(($int >> 24) & 0xff) .
 773              self::intToChr($hiB & 0xff) .
 774              self::intToChr(($hiB >>  8) & 0xff) .
 775              self::intToChr(($hiB >> 16) & 0xff) .
 776              self::intToChr(($hiB >> 24) & 0xff);
 777      }
 778  
 779      /**
 780       * Safe string length
 781       *
 782       * @internal You should not use this directly from another application
 783       *
 784       * @ref mbstring.func_overload
 785       *
 786       * @param string $str
 787       * @return int
 788       * @throws TypeError
 789       */
 790      public static function strlen($str)
 791      {
 792          /* Type checks: */
 793          if (!is_string($str)) {
 794              throw new TypeError('String expected');
 795          }
 796  
 797          return (int) (
 798          self::isMbStringOverride()
 799              ? mb_strlen($str, '8bit')
 800              : strlen($str)
 801          );
 802      }
 803  
 804      /**
 805       * Turn a string into an array of integers
 806       *
 807       * @internal You should not use this directly from another application
 808       *
 809       * @param string $string
 810       * @return array<int, int>
 811       * @throws TypeError
 812       */
 813      public static function stringToIntArray($string)
 814      {
 815          if (!is_string($string)) {
 816              throw new TypeError('String expected');
 817          }
 818          /**
 819           * @var array<int, int>
 820           */
 821          $values = array_values(
 822              unpack('C*', $string)
 823          );
 824          return $values;
 825      }
 826  
 827      /**
 828       * Safe substring
 829       *
 830       * @internal You should not use this directly from another application
 831       *
 832       * @ref mbstring.func_overload
 833       *
 834       * @param string $str
 835       * @param int $start
 836       * @param int $length
 837       * @return string
 838       * @throws TypeError
 839       */
 840      public static function substr($str, $start = 0, $length = null)
 841      {
 842          /* Type checks: */
 843          if (!is_string($str)) {
 844              throw new TypeError('String expected');
 845          }
 846  
 847          if ($length === 0) {
 848              return '';
 849          }
 850  
 851          if (self::isMbStringOverride()) {
 852              if (PHP_VERSION_ID < 50400 && $length === null) {
 853                  $length = self::strlen($str);
 854              }
 855              $sub = (string) mb_substr($str, $start, $length, '8bit');
 856          } elseif ($length === null) {
 857              $sub = (string) substr($str, $start);
 858          } else {
 859              $sub = (string) substr($str, $start, $length);
 860          }
 861          if ($sub !== '') {
 862              return $sub;
 863          }
 864          return '';
 865      }
 866  
 867      /**
 868       * Compare a 16-character byte string in constant time.
 869       *
 870       * @internal You should not use this directly from another application
 871       *
 872       * @param string $a
 873       * @param string $b
 874       * @return bool
 875       * @throws SodiumException
 876       * @throws TypeError
 877       */
 878      public static function verify_16($a, $b)
 879      {
 880          /* Type checks: */
 881          if (!is_string($a)) {
 882              throw new TypeError('String expected');
 883          }
 884          if (!is_string($b)) {
 885              throw new TypeError('String expected');
 886          }
 887          return self::hashEquals(
 888              self::substr($a, 0, 16),
 889              self::substr($b, 0, 16)
 890          );
 891      }
 892  
 893      /**
 894       * Compare a 32-character byte string in constant time.
 895       *
 896       * @internal You should not use this directly from another application
 897       *
 898       * @param string $a
 899       * @param string $b
 900       * @return bool
 901       * @throws SodiumException
 902       * @throws TypeError
 903       */
 904      public static function verify_32($a, $b)
 905      {
 906          /* Type checks: */
 907          if (!is_string($a)) {
 908              throw new TypeError('String expected');
 909          }
 910          if (!is_string($b)) {
 911              throw new TypeError('String expected');
 912          }
 913          return self::hashEquals(
 914              self::substr($a, 0, 32),
 915              self::substr($b, 0, 32)
 916          );
 917      }
 918  
 919      /**
 920       * Calculate $a ^ $b for two strings.
 921       *
 922       * @internal You should not use this directly from another application
 923       *
 924       * @param string $a
 925       * @param string $b
 926       * @return string
 927       * @throws TypeError
 928       */
 929      public static function xorStrings($a, $b)
 930      {
 931          /* Type checks: */
 932          if (!is_string($a)) {
 933              throw new TypeError('Argument 1 must be a string');
 934          }
 935          if (!is_string($b)) {
 936              throw new TypeError('Argument 2 must be a string');
 937          }
 938  
 939          return (string) ($a ^ $b);
 940      }
 941  
 942      /**
 943       * Returns whether or not mbstring.func_overload is in effect.
 944       *
 945       * @internal You should not use this directly from another application
 946       *
 947       * Note: MB_OVERLOAD_STRING === 2, but we don't reference the constant
 948       * (for nuisance-free PHP 8 support)
 949       *
 950       * @return bool
 951       */
 952      protected static function isMbStringOverride()
 953      {
 954          static $mbstring = null;
 955  
 956          if ($mbstring === null) {
 957              if (!defined('MB_OVERLOAD_STRING')) {
 958                  $mbstring = false;
 959                  return $mbstring;
 960              }
 961              $mbstring = extension_loaded('mbstring')
 962                  && defined('MB_OVERLOAD_STRING')
 963                  &&
 964              ((int) (ini_get('mbstring.func_overload')) & 2);
 965              // MB_OVERLOAD_STRING === 2
 966          }
 967          /** @var bool $mbstring */
 968  
 969          return $mbstring;
 970      }
 971  }


Generated : Sat Dec 21 08:20:01 2024 Cross-referenced by PHPXref