[筆記] Faker 如何產生 10 位數以上的 random number?
在寫 Laravel 測試需要的 Factory 時,有個欄位需要 16 位數以下的數字。一般來說直接透過 fzaninotto/Faker 的 randomNumber
就可以很方便的生成了:
$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
來決定位數。如果你有其他不錯的作法,也歡迎在下方留言告訴我~