Skip to content

Commit bb7204d

Browse files
committed
fix: use random_byte() in random_string() alnum,nozero,alpha
1 parent ece0c76 commit bb7204d

1 file changed

Lines changed: 64 additions & 1 deletion

File tree

system/Helpers/text_helper.php

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ function random_string(string $type = 'alnum', int $len = 8): string
559559
break;
560560
}
561561

562-
return substr(str_shuffle(str_repeat($pool, (int) ceil($len / strlen($pool)))), 0, $len);
562+
return _from_random($len, $pool);
563563

564564
case 'numeric':
565565
$max = 10 ** $len - 1;
@@ -587,6 +587,69 @@ function random_string(string $type = 'alnum', int $len = 8): string
587587
}
588588
}
589589

590+
if (! function_exists('_from_random')) {
591+
/**
592+
* The following function was derived from code of Symfony (v6.2.7 - 2023-02-28)
593+
* https://github.com/symfony/symfony/blob/80cac46a31d4561804c17d101591a4f59e6db3a2/src/Symfony/Component/String/ByteString.php#L45
594+
* Code subject to the MIT license (https://github.com/symfony/symfony/blob/v6.2.7/LICENSE).
595+
* Copyright (c) 2004-present Fabien Potencier
596+
*
597+
* The following method was derived from code of the Hack Standard Library (v4.40 - 2020-05-03)
598+
* https://github.com/hhvm/hsl/blob/80a42c02f036f72a42f0415e80d6b847f4bf62d5/src/random/private.php#L16
599+
* Code subject to the MIT license (https://github.com/hhvm/hsl/blob/master/LICENSE).
600+
* Copyright (c) 2004-2020, Facebook, Inc. (https://www.facebook.com/)
601+
*
602+
* @internal Outside the framework this should not be used directly.
603+
*/
604+
function _from_random(int $length, string $pool): string
605+
{
606+
if ($length <= 0) {
607+
throw new InvalidArgumentException(
608+
sprintf('A strictly positive length is expected, "%d" given.', $length)
609+
);
610+
}
611+
612+
$poolSize = \strlen($pool);
613+
$bits = (int) ceil(log($poolSize, 2.0));
614+
if ($bits <= 0 || $bits > 56) {
615+
throw new InvalidArgumentException(
616+
'The length of the alphabet must in the [2^1, 2^56] range.'
617+
);
618+
}
619+
620+
$string = '';
621+
622+
while ($length > 0) {
623+
$urandomLength = (int) ceil(2 * $length * $bits / 8.0);
624+
$data = random_bytes($urandomLength);
625+
$unpackedData = 0;
626+
$unpackedBits = 0;
627+
628+
for ($i = 0; $i < $urandomLength && $length > 0; $i++) {
629+
// Unpack 8 bits
630+
$unpackedData = ($unpackedData << 8) | \ord($data[$i]);
631+
$unpackedBits += 8;
632+
633+
// While we have enough bits to select a character from the alphabet, keep
634+
// consuming the random data
635+
for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) {
636+
$index = ($unpackedData & ((1 << $bits) - 1));
637+
$unpackedData >>= $bits;
638+
// Unfortunately, the alphabet size is not necessarily a power of two.
639+
// Worst case, it is 2^k + 1, which means we need (k+1) bits and we
640+
// have around a 50% chance of missing as k gets larger
641+
if ($index < $poolSize) {
642+
$string .= $pool[$index];
643+
$length--;
644+
}
645+
}
646+
}
647+
}
648+
649+
return $string;
650+
}
651+
}
652+
590653
if (! function_exists('increment_string')) {
591654
/**
592655
* Add's _1 to a string or increment the ending number to allow _2, _3, etc

0 commit comments

Comments
 (0)