Статьи

Генерация случайных буквенно-цифровых строк в PHP

Позвольте мне начать этот пост с того, что почти ни одно событие не является действительно случайным. Даже исход классического броска монеты можно было бы теоретически предсказать, если бы мы знали влияние каждого из факторов, таких как трение в воздухе, гравитация и начальная сила.

То же самое применимо к генерации случайных чисел и буквенно-цифровых строк. Лучшее, на что мы можем надеяться, — это генерировать числа и строки, которые не соответствуют шаблону и не могут быть практически предсказаны злоумышленником.

В этом уроке мы рассмотрим различные методы генерации случайных чисел и буквенно-цифровых строк в PHP. Некоторые из них будут криптографически безопасными, в то время как другие предназначены только для случайного использования, например, для присвоения псевдослучайных имен файлов или создания URL-адресов и предложения имен пользователей.

В PHP есть три разные функции для генерации случайных чисел. Все они примут минимально и максимально возможное значение для случайных чисел и выведут случайное число для вас. Это rand($min, $max) , mt_rand($min, $max) и random_int($min, $max) .

С помощью rand() минимальное и максимальное значения целых чисел, которые вы можете сгенерировать, лежат между 0 и значением, возвращаемым getrandmax() . До PHP 7.1.0 эта функция была примерно в четыре раза медленнее, чем mt_rand() . Однако, начиная с PHP 7.1.0, он стал псевдонимом mt_rand() . В отличие от mt_rand() , вы можете установить значение $max меньше, чем $min не вызывая ошибки.

С помощью mt_rand() минимальное и максимальное значения целых чисел, которые вы можете сгенерировать, лежат между 0 и значением, возвращаемым mt_getrandmax() . Он использует реализацию Mersenne Twister для генерации случайных чисел. Обратите внимание: до PHP 7.1.0 эта функция реализовала неверную версию алгоритма для генерации чисел. Тем не менее, это было исправлено в более новых версиях.

Функция стала еще лучше в PHP 7.2.0, избавившись от ошибки смещения по модулю. Это означает, что для некоторых конкретных семян ваша последовательность случайных чисел теперь будет немного лучше по сравнению со старыми версиями. Тем не менее, некоторый специализированный код может действительно полагаться на это смещение Если это так, вы можете использовать более старый начальный алгоритм, вызвав mt_srand() для MT_RAND_PHP генератора случайных чисел и передав MT_RAND_PHP в качестве значения второго параметра.

Функция mt_rand() имеет период 2 19937 -1, что в основном означает, что в лучших случаях вы получаете до 2 19937 -1 случайных чисел, прежде чем последовательность начнет повторяться. Следует отметить, что повторение последовательности не совпадает с повторением определенного числа. Другими словами, вы можете получить одно и то же случайное число дважды, но это не означает, что сама последовательность начала повторяться. Следующая последовательность является примером:

1
187 3276 1267 15 1267 34598 3467 125 17

В приведенной выше последовательности у нас было 1267 дважды на выходе, но это не значит, что вся последовательность начала повторяться после этого. Маловероятно, что такое же число будет повторяться так скоро в случайной последовательности, но это возможно!

Если вы хотите криптографически защищенные псевдослучайные числа, random_int() в PHP — ваш лучший выбор. Он будет генерировать случайные числа между предоставленными значениями $min и $max , которые по умолчанию PHP_INT_MIN и PHP_INT_MAX . К сожалению, эта функция доступна только начиная с PHP 7.0. Для версий до этого вы можете использовать этот polyfill на GitHub .

Вместо генерации случайных целых чисел вы также можете генерировать числа с плавающей точкой. Это можно сделать без особых усилий, просто разделив случайное число на значение, возвращаемое mt_getrandmax() . В следующем примере будет показано, как генерировать случайное число с плавающей точкой между 0 и 1 или между любыми другими минимальными и максимальными пределами.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<?php
 
// Output: 0.69458310943776
echo mt_rand(0, mt_getrandmax())/mt_getrandmax();
 
function mt_random_float($min, $max) {
    $float_part = mt_rand(0, mt_getrandmax())/mt_getrandmax();
    $integer_part = mt_rand($min, $max — 1);
    return $integer_part + $float_part;
}
 
// Output: 10.199064863938
echo mt_random_float(10, 11);
 
// Output: 35.540808309121
echo mt_random_float(15, 50);
 
?>

При генерации случайного числа с плавающей точкой между заданными пределами мы убеждаемся, что случайные целые числа не превышают $max - 1 . Таким образом, мы можем быть уверены, что добавление части с плавающей запятой не займет число, превышающее максимальный предел.

Одна концепция, которая нуждается в небольшом объяснении, — это семена. Проще говоря, это просто числа, которые можно использовать для инициализации функций rand() и mt_rand() перед генерацией любых случайных чисел. Функция, которая заполняет rand() , называется srand($seed) , а функция, которая mt_srand($seed, $mode) mt_rand() , называется mt_srand($seed, $mode) .

Важно помнить, что предоставление начального начального значения каждый раз перед вызовом rand() и mt_rand() не обязательно даст лучшие случайные числа. Фактически, использование одного и того же начального числа каждый раз даст вам одно и то же случайное число!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?php
 
mt_srand(10);
// Output: 1656398468
echo mt_rand();
 
mt_srand(10);
// Output: 1656398468
echo mt_rand();
 
mt_srand(10);
// Output: 1656398468
echo mt_rand();
 
?>

Заполнение случайного числа полезно в ситуациях, когда вы хотите создать случайную, но воспроизводимую последовательность. Следующий фрагмент кода генерирует ту же последовательность случайных чисел при двойном запуске.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<?php
 
mt_srand(10);
 
$count = 0;
 
while($count < 10) {
    echo mt_rand(0, 100).» «;
    $count++;
}
 
// Output on First Run:
// 68 58 68 13 3 48 30 37 96 82
 
// Output on Second Run:
// 68 58 68 13 3 48 30 37 96 82

Генерация воспроизводимых случайных последовательностей таким способом может помочь отладке программ, которые тестировались с использованием случайных данных — если вы отслеживаете начальное число, вы можете воспроизвести тот же ввод, чтобы выяснить, что пошло не так.

Существует множество способов генерации случайных буквенно-цифровых строк, и то, что вы используете, будет зависеть от ваших потребностей.

Если вы хотите генерировать случайные буквенно-цифровые строки из фиксированного набора символов, вы можете использовать str_shuffle($string) . Эта функция предоставит вам случайно перемешанную строку. Начиная с PHP 7.1, алгоритм, который определяет случайный порядок символов в выходной строке, был изменен на Mersenne Twister.

Помните, что случайная строка, сгенерированная таким образом, не является криптографически безопасной. Тем не менее, строка все равно будет довольно непредсказуемой для общего использования, например, для генерации случайных имен файлов или URL-адресов. Вот несколько примеров:

01
02
03
04
05
06
07
08
09
10
11
<?php
 
$permitted_chars = ‘0123456789abcdefghijklmnopqrstuvwxyz’;
// Output: 54esmdr0qf
echo substr(str_shuffle($permitted_chars), 0, 10);
 
$permitted_chars = ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’;
// Output: video-g6swmAP8X5VG4jCi.mp4
echo ‘video-‘.substr(str_shuffle($permitted_chars), 0, 16).’.mp4′;
 
?>

Ваш вывод, скорее всего, будет отличаться в обоих случаях. В первом случае мы просто перетасовали строку разрешенных символов, а затем взяли первые 10 символов. Во втором случае мы добавили «video» в начале сгенерированной строки и «.mp4» в конце.

Этот метод генерации случайных буквенно-цифровых строк очень прост, но у него есть несколько проблем. Например, вы никогда не получите одинаковые символы в вашей случайной строке дважды. Кроме того, длина случайной выходной строки может быть такой же, как и длина входной строки.

Если проблемы, которые я перечислил выше, нарушают условия соглашения, возможно, вы захотите взглянуть на некоторые другие реализации. Следующий код решит обе эти проблемы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
 
$permitted_chars = ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’;
 
function generate_string($input, $strength = 16) {
    $input_length = strlen($input);
    $random_string = »;
    for($i = 0; $i < $strength; $i++) {
        $random_character = $input[mt_rand(0, $input_length — 1)];
        $random_string .= $random_character;
    }
 
    return $random_string;
}
 
// Output: iNCHNGzByPjhApvn7XBD
echo generate_string($permitted_chars, 20);
 
// Output: 70Fmr9mOlGID7OhtTbyj
echo generate_string($permitted_chars, 20);
 
// Output: Jp8iVNhZXhUdSlPi1sMNF7hOfmEWYl2UIMO9YqA4faJmS52iXdtlA3YyCfSlAbLYzjr0mzCWWQ7M8AgqDn2aumHoamsUtjZNhBfU
echo generate_string($permitted_chars, 100);
 
?>

Вы можете изменить его, чтобы добавить определенные суффиксы и префиксы к сгенерированной случайной строке. Люди, использующие PHP 7, могут улучшить генерацию строк, используя криптографически безопасную функцию random_int() вместо mt_rand() .

Если вы хотите генерировать случайные шестнадцатеричные строки в PHP, вы также можете использовать md5($string, $raw_output) или sha1($string, $raw_output) . Оба они будут генерировать хеши заданной входной строки.

Вы будете получать уникальные хэши, пока входные данные уникальны. Этого можно достичь, используя в качестве входных данных функцию типа time() . По умолчанию md5() возвращает шестнадцатеричную строку из 32 символов, а sha1() возвращает шестнадцатеричную строку из 40 символов. Они могут быть обрезаны до определенной длины с помощью функции substr() .

Вот пример вывода, возвращаемого этими функциями:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?php
 
// Output: 36e5e490f14b031e
echo substr(md5(time()), 0, 16);
 
// Output: aa88ef597c77a5b3
echo substr(sha1(time()), 0, 16);
 
// Output: 447c13ce896b820f353bec47248675b3
echo md5(time());
 
// Output: 6c2cef9fe21832a232da7386e4775654b77c7797
echo sha1(time());
 
?>

Как видите, генерировать случайные и уникальные шестнадцатеричные строки длиной до 40 символов в PHP очень просто.

Три функции для генерации случайных буквенно-цифровых строк, которые мы обсуждали до сих пор, не являются криптографически безопасными. К счастью, в PHP также есть функция random_bytes($length) для генерации криптографически безопасных псевдослучайных байтов. Параметр $length определяет, какой длины должна быть выходная строка.

Получив выходные данные в виде случайных байтов, вы можете использовать bin2hex() чтобы преобразовать их в шестнадцатеричные значения. Это удвоит длину строки.

01
02
03
04
05
06
07
08
09
10
11
12
<?php
 
// Output: b7b33efa07915b60ad55
echo bin2hex(random_bytes(10));
 
// Output: a2e6cb1f25616324c8a11a2cceb0b47c590949ea
echo bin2hex(random_bytes(20));
 
// Output: 25af3b86e11884ef5e8ef70a0ad06cba81b89ed6af3781a0
echo bin2hex(random_bytes(24));
 
?>

Другая функция, которую вы можете использовать для генерации криптографически безопасных случайных байтов, — это openssl_random_pseudo_bytes($length, &$crypto_strong) . Значение второго параметра можно использовать для определения, будет ли выходная строка генерироваться с использованием криптографически безопасного алгоритма или нет.

В этом уроке мы рассмотрели генерацию случайных чисел и буквенно-цифровых строк в PHP. Генерация случайных чисел может быть полезна в различных ситуациях, например, в играх, где вам нужно порождать вражеских игроков или случайным образом подсказывать пользователям буквы, чтобы они могли составить целое слово.

Как и случайные числа, генерация случайных буквенно-цифровых строк также может быть очень полезна при многих обстоятельствах. С помощью str_shuffle() вы можете выбрать, какой набор символов будет отображаться в ваших случайных строках. С помощью sha1() и md5() вы можете легко генерировать случайные шестнадцатеричные последовательности, а с помощью random_bytes() вы можете создавать криптографически безопасные строки. Это позволит вам генерировать значимые, но рандомизированные имена файлов и имена пользователей, которые трудно угадать.

Я надеюсь, вам понравился этот урок. Если у вас есть какие-либо вопросы, не стесняйтесь задавать их в комментариях.