0

Using php7, I'd like to generate a random variable length unsigned bigint(8) number - between 1 and 18446744073709551615 I found https://stackoverflow.com/a/28071695/836888 but mcrypt_create_iv has been deprecated in php 7.

Thanks

Rad
  • 5
  • 3
  • Does this answer your question? [Random number in range \[min - max\] using PHP](https://stackoverflow.com/questions/4173867/random-number-in-range-min-max-using-php) – Kaddath Aug 20 '20 at 10:01
  • Thanks, looked at the answers but none seem to generate random numbers up to the upper limit of an unsigned bigint but stop at PHP_INT_MAX. – Rad Aug 20 '20 at 10:25
  • The following seems to work well enought to cover the whole range or unsigned bigint(8) numbers. echo mt_rand(0, PHP_INT_MAX) + mt_rand(0, PHP_INT_MAX); when PHP_INT_MAX is 9223372036854775807 – Rad Aug 20 '20 at 10:36
  • As far as I know in PHP you cannot have an integer bigger than `PHP_INT_MAX` (its value changes depending on the machine), and PHP doesn't support unsigned integers. So `mt_rand(0, PHP_INT_MAX) + mt_rand(0, PHP_INT_MAX);` has a big chance of overflowing integer limits. Are you talking about genrating a string, or use floats? (in which case you should edit your question to add this detail) – Kaddath Aug 20 '20 at 11:09
  • By bigint(8) I meant the mysql column type that can store values in the range 0 - 18446744073709551615 (when unsigned). I needed php to generate a random number in this range to be stored in this mysql column and serve as an ID. I didn't want to have sequential id's that can easily be guessed / looped over - hence the need for randomness. – Rad Aug 20 '20 at 12:21
  • Ok this looks a lot like an XY problem, you should probably add more details about what in your code could be `guessed / looped over` and why it could be an issue. The big problem with generating random ids is you need to assure first that it's not already used or it could lead to a duplicate key mysql error.. – Kaddath Aug 20 '20 at 13:05
  • My only problem is using PHP 7 to generate a random ID number in the range 0 - 18446744073709551615 that can be saved into a bigint(8) mysql column. Duplicates are not an issue - once an ID is generated, I check if it is unique and if it's not, I generate a new one. – Rad Aug 20 '20 at 13:25

2 Answers2

1

Short answer, gmp_random_range():

$from = 0;
$to = '18446744073709551615';
$random = gmp_random_range($from, $to);
var_dump($random, (string)$random);

E.g.:

object(GMP)#1 (1) {
  ["num"]=>
  string(20) "10366718409313808674"
}
string(20) "10366718409313808674"

Additional digressions:

  • MySQL's BIGINT is an 8-byte integer, what means -263 to 263-1 if signed and 0 to 264-1 if unsigned.

  • PHP_INT_MAX is a 4-byte value in 32-bit builds but an 8-byte value in 64-bit builds. It's signed in either case.

So if you could safely assume 64-bit PHP and you wanted signed numbers then you'd be basically done with any random generation function. You could use random_int() if you're after cryptographically secure numbers or mt_rand() otherwise:

var_dump(random_int(0, PHP_INT_MAX));
var_dump(mt_rand(0, PHP_INT_MAX));

But you want unsigned values, thus you must switch to strings. Once there, an obvious approach is to generate two integers and concatenate them as strings—the tricky part is ensuring you don't overflow your range. As alternative, you can use the GMP arbitrary precision extension, which happily has a dedicated function.

P.S. You actually mention BIGINT(8). That 8 is nothing but the display size when printing from compliant clients, it doesn't represent the storage range and it isn't enforced by any mean. And since you clearly state you expect up to 20 digits, it's just misleading.

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • You're absolutely right, I should have written BIGINT(20) rather than BIGINT(8). – Rad Aug 20 '20 at 17:10
0

This is a function you can use to generate number strings from 1 to any maximum value exceeding PHP_INT_MAX (both maximum and result will have to be strings)

IMPORTANT: Do not use this for cryptography, as not all numbers don't have equal chances to be picked:

  • chance for result '1' is doubled as we default zero to '1'
  • we randomy choose each char, so depending on the maximum chosen, not all numbers have the same chance
<?php
function notCryptoRandomDecimalString($maxStrValue){
    $result = '';
    $maxBegin = '';
    
    for($i = 0; $i < strlen($maxStrValue); $i++){
        $maxDigit = $maxStrValue[$i];
        //if beginning of the result is same than beginning of the max,
        //we limit random to current char from maximum, or else it can be from 0 to 9
        if($result === $maxBegin){
            $result .= random_int(0, $maxDigit);
        }else{
            $result .= random_int(0, 9);
        }
        $maxBegin .= $maxDigit;
    }
    
    //remove leading zeroes
    $result = ltrim($result, '0');
    
    //if zero was picked, default to 1
    if($result === ''){
        $result = '1';
    }
    
    return $result;
}

echo(notCryptoRandomDecimalString('18446744073709551615'));
Kaddath
  • 5,933
  • 1
  • 9
  • 23