[筆記] Faker 如何產生 10 位數以上的 random number?

在寫 Laravel 測試需要的 Factory 時,有個欄位需要 16 位數以下的數字。一般來說直接透過 fzaninotto/FakerrandomNumber 就可以很方便的生成了:

$number = $faker->randomNumber(16);

結果執行的時候竟然噴錯了!

InvalidArgumentException: randomNumber() can only generate numbers up to mt_getrandmax()

mt_getrandmax() 是什麼

錯誤訊息中提到的 mt_getrandmax() 是 PHP 的內建函式,會回傳 PHP 透過 mt_rand() 可以產生的最大數。

這個 mt_rand() 就是一般的隨機產生函式,只要有指定 min 和 max,其實他是可以產生更大數的,不是很理解為什麼 Faker 沒有做這個處理。

可以怎麼做?

這裡我想了幾個方法,可以給各位參考。

方法一:直接使用 mt_rand 或 random_int

既然他背後是使用 mt_rand(),那就直接使用它吧。

$number = mt_rand(1, 9999999999999999);

不過我實際使用發現,雖然我是只有上限給了 16 位數,他產生的結果好像是固定十六位數。

後來嘗試了 PHP 7 新的 random_int,結果差不多。

$number = random_int(1, 9999999999999999);

PHP 官網文件在 mt_getrandmax 的頁面上有提及,如果提供 MAX 給 mt_rand 會減少隨機性,不知道是不是這個原因。


方法二:使用 numberBetween

Faker 有提供 numberBetween,所以可以直接使用:

$number = $faker->numberBetween(1, 9999999999999999);

因為 numberBetween 也是基於 mt_rand,我實測結果好像只比 mt_rand 好一點。

以上兩個方法如果有人知道如何修正,再麻煩留言跟我說一下。


方法三:使用 numerify

這個是 Faker 作者在 類似的 issue 裡提出的做法,我把它替換成這個情境的解法。

numerify 是 Faker 的另一個函式,可以在字串的指定位置裡塞入數字。官方範例:

numerify('Hello ###') // 'Hello 609'

搭配 PHP 內建的 str_repeat,可以重複指定次數的字串。然後我們再搭配 Faker 的 numberBetween,就組成以下的程式:

$numberLength = $faker->numberBetween(1, 16);
$number = $faker->numerify(str_repeat('#', $numberLength));

這次的隨機性應該比較高了。

後記

我最終採用了方法三。我還有查到有些人pow 來決定位數。如果你有其他不錯的作法,也歡迎在下方留言告訴我~