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


Generated : Fri Oct 10 08:20:03 2025 Cross-referenced by PHPXref