[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Oct 10 08:20:03 2025 | Cross-referenced by PHPXref |