Короткий уникальный идентификатор в php

Я хочу создать уникальный идентификатор, но uniqid() дает что-то вроде '492607b0ee414'. Я бы хотел что-то похожее на то, что дает tinyurl: '64k8ra'. Чем короче, тем лучше. Единственное требование - в нем не должно быть очевидного порядка и он должен выглядеть красивее, чем кажущаяся случайной последовательность чисел. Буквы предпочтительнее цифр, и в идеале это не было бы смешанным регистром. Поскольку количество записей не будет таким большим (до 10000 или около того), риск столкновения не является большим фактором.

Любые предложения приветствуются.

Вы нашли решение? Если да, поделитесь им или наградите ответом.

Till 02.06.2009 05:52

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

Antti 08.10.2009 02:11

Поскольку uniqid основан на метке времени, первые 6 символов будут одинаковыми в течение довольно долгого времени;) Даже если я взял последние x символов или скомбинировал их каким-то образом, я думаю, что все еще есть более чистый подход. Что-нибудь вроде "x1f" было бы неплохо.

Antti 21.11.2008 04:09

Если вам нужны случайные, короткие, неупорядоченные строчные строки, состоящие только из букв, вы можете получить строки с Random::alphaLowercaseString(6) или длиной 8 или 10, по вашему желанию.

caw 20.11.2019 18:28
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
48
4
74 791
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Есть два способа получить надежно уникальный идентификатор: сделать его настолько длинным и переменным, чтобы шансы столкновения были чрезвычайно малы (как в случае с GUID), или сохранить все сгенерированные идентификаторы в таблице для поиска (либо в памяти, либо в базе данных). или файл) для проверки уникальности при генерации.

Если вы действительно спрашиваете, как вы можете сгенерировать такой короткий ключ и гарантировать его уникальность без какой-либо проверки дубликатов, ответ: вы не можете.

Буквы красивые, цифры некрасивые. Вам нужны случайные строки, но не нужны «уродливые» случайные строки?

Создайте случайное число и распечатайте его в альфа-стиль (база-26), как "числа" резервирования, которые дают авиакомпании.

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

Другой вариант: использовать uniqid() и избавиться от цифр.

function strip_digits_from_string($string) {
    return preg_replace('/[0-9]/', '', $string);
}

Или замените их буквами:

function replace_digits_with_letters($string) {
    return strtr($string, '0123456789', 'abcdefghij');
}

Это довольно близко к тому, что я хочу. Идентификаторы авиабилетов также являются хорошим примером. В основном то, что я хочу, - это хороший способ создать этот случайный код из 3-5 символов / цифр, который я затем могу преобразовать в строку. В остальном Uniqid подходит, только слишком долго.

Antti 21.11.2008 05:56
Ответ принят как подходящий

Сделайте небольшую функцию, которая возвращает случайные буквы заданной длины:

<?php
function generate_random_letters($length) {
    $random = '';
    for ($i = 0; $i < $length; $i++) {
        $random .= chr(rand(ord('a'), ord('z')));
    }
    return $random;
}

Затем вы захотите вызвать это, пока оно не станет уникальным, в псевдокоде, в зависимости от того, где вы храните эту информацию:

do {
    $unique = generate_random_letters(6);
} while (is_in_table($unique));
add_to_table($unique);

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

Обновлено: Я бы также добавил, что это имеет смысл только в том случае, если, поскольку вы собираетесь его использовать, это не для большого количества элементов, потому что это может стать довольно медленным, чем больше вы получите столкновений (получение идентификатора уже в таблице). Конечно, вам понадобится индексированная таблица, и вы захотите настроить количество букв в идентификаторе, чтобы избежать конфликтов. В этом случае с 6 буквами у вас будет 26 ^ 6 = 308915776 возможных уникальных идентификаторов (без плохих слов), которых должно хватить для вашей потребности в 10000.

Обновлено: Если вам нужны комбинации букв и цифр, вы можете использовать следующий код:

$random .= rand(0, 1) ? rand(0, 9) : chr(rand(ord('a'), ord('z')));

Вы должны поместить ord('a') и ord('z') вне цикла, чтобы избежать вызова функции на каждом проходе.

Scalpweb 05.10.2015 18:25

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

function toUId($baseId, $multiplier = 1) {
    return base_convert($baseId * $multiplier, 10, 36);
}
function fromUId($uid, $multiplier = 1) {
    return (int) base_convert($uid, 36, 10) / $multiplier;
}

echo toUId(10000, 11111);
1u5h0w
echo fromUId('1u5h0w', 11111);
10000

Умные люди, вероятно, смогут понять это с достаточным количеством примеров идентификаторов. Не позволяйте этой безвестности заменить безопасность.

Есть ли способ использовать base_convert () для включения букв верхнего и нижнего регистра и 0-9? Base_convert ($ uid, 62, 10) работает?

JoshFinnie 06.05.2009 21:27

JoshFinnie: вам нужно будет создать свою собственную чувствительную к регистру функцию для более высоких базовых значений, чем 36.

OIS 06.05.2009 22:04
function rand_str($len = 12, $type = '111', $add = null) {
    $rand = ($type[0] == '1'  ? 'abcdefghijklmnpqrstuvwxyz' : '') .
            ($type[1] == '1'  ? 'ABCDEFGHIJKLMNPQRSTUVWXYZ' : '') .
            ($type[2] == '1'  ? '123456789'                 : '') .
            (strlen($add) > 0 ? $add                        : '');

    if (empty($rand)) $rand = sha1( uniqid(mt_rand(), true) . uniqid( uniqid(mt_rand(), true), true) );

    return substr(str_shuffle( str_repeat($rand, 2) ), 0, $len);
}

Вот процедура, которую я использую для случайных base62 любой длины ...

Вызов gen_uuid() возвращает такие строки, как WJX0u0jV, E9EMaZ3P и т. д.

По умолчанию это возвращает 8 цифр, следовательно, интервал составляет 64 ^ 8 или примерно 10 ^ 14, этого часто бывает достаточно, чтобы столкновения были довольно редкими.

Для большей или меньшей строки передайте по желанию $ len. Нет ограничений по длине, поскольку я добавляю до тех пор, пока не буду удовлетворен [до предела безопасности 128 символов, который можно удалить].

Обратите внимание: используйте случайную соль внутри в md5 [или sha1, если хотите], так что это не может быть легко реконструировано.

Я не нашел никаких надежных преобразований base62 в Интернете, поэтому этот подход удаления символов из результата base64.

Бесплатное использование под лицензией BSD, наслаждаться,

горд

function gen_uuid($len=8)
{
    $hex = md5("your_random_salt_here_31415" . uniqid("", true));

    $pack = pack('H*', $hex);

    $uid = base64_encode($pack);        // max 22 chars

    $uid = ereg_replace("[^A-Za-z0-9]", "", $uid);    // mixed case
    //$uid = ereg_replace("[^A-Z0-9]", "", strtoupper($uid));    // uppercase only

    if ($len<4)
        $len=4;
    if ($len>128)
        $len=128;                       // prevent silliness, can remove

    while (strlen($uid)<$len)
        $uid = $uid . gen_uuid(22);     // append until length achieved

    return substr($uid, 0, $len);
}

для версии в верхнем регистре используйте вместо нее эту строку - $ uid = ereg_replace ("[" A-Z0-9] "," ", strtoupper ($ uid));

gord 04.10.2009 17:46

Случайно, но если соль была переменной, есть ли способ отменить это, чтобы найти переменную соли?

Jon 21.09.2012 04:38

Действительно простое решение:

Сделайте уникальный идентификатор с помощью:

$id = 100;
base_convert($id, 10, 36);

Получите снова исходное значение:

intval($str,36);

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

Это полностью не соответствует требованиям "Единственное требование - не иметь очевидного порядка"

Kaktus 31.12.2017 05:20

@gen_uuid () пользователя gord.

У preg_replace были неприятные проблемы с utf-8, из-за которых uid иногда содержал "+" или "/". Чтобы обойти это, вы должны явно указать шаблон utf-8

function gen_uuid($len=8) {

    $hex = md5("yourSaltHere" . uniqid("", true));

    $pack = pack('H*', $hex);
    $tmp =  base64_encode($pack);

    $uid = preg_replace("#(*UTF8)[^A-Za-z0-9]#", "", $tmp);

    $len = max(4, min(128, $len));

    while (strlen($uid) < $len)
        $uid .= gen_uuid(22);

    return substr($uid, 0, $len);
}

Мне потребовалось довольно много времени, чтобы найти это, возможно, это избавит кого-то от головной боли

Я придумал, как мне кажется, довольно крутое решение, делающее это без проверки уникальности. Я думал, что поделюсь с будущими посетителями.

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

Это python, а не php, но я загрузил здесь код: https://github.com/adecker89/Tiny-Unique-Identifiers

Если вам нравится более длинная версия уникального идентификатора, используйте это:
$ uniqueid = sha1 (md5 (время ()));

Вы также можете сделать это так:

public static function generateCode($length = 6)
    {
        $az = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $azr = rand(0, 51);
        $azs = substr($az, $azr, 10);
        $stamp = hash('sha256', time());
        $mt = hash('sha256', mt_rand(5, 20));
        $alpha = hash('sha256', $azs);
        $hash = str_shuffle($stamp . $mt . $alpha);
        $code = ucfirst(substr($hash, $azr, $length));
        return $code;
    }

Вы можете делать эти нечистые / дорогостоящие вещи без, такие как циклы, конкатенации строк или множественные вызовы rand (), в чистом и удобном для чтения виде. Также лучше использовать mt_rand():

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    return dechex($random);
}

Если вам нужно, чтобы строка имела точную длину в любом случае, просто добавьте к шестнадцатеричному числу нули:

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    $number = dechex($random);
    return str_pad($number, $length, '0', STR_PAD_LEFT);
}

«Теоретический недостаток» заключается в том, что вы ограничены возможностями PHP - но в этом случае это скорее философский вопрос;) Давайте все равно рассмотрим его:

  • PHP ограничен тем, что он может представлять в виде шестнадцатеричного числа, делая это таким образом. Это будет $length <= 8по меньшей мере в 32-битной системе, где ограничение PHP для этого должно быть 4.294.967.295.
  • Генератор случайных чисел PHP также имеет максимум. Для mt_rand()по меньшей мере в 32-битной системе это должно быть 2.147.483.647.
  • Таким образом, вы теоретически ограничены идентификаторами 2.147.483.647.

Возвращаясь к теме - интуитивно понятный do { (generate ID) } while { (id is not uniqe) } (insert id) имеет один недостаток и один возможный недостаток, который может увести вас в темноту ...

Недостаток: Проверка пессимистична. Для этого всегда требует проверки в базе данных. Наличие достаточного пространства ключей (например, длина 5 для ваших 10k записей) вряд ли вызовет коллизии так же часто, поскольку сравнительно может потреблять меньше ресурсов, чтобы просто попытаться сохранить данные и повторить попытку только в случае ошибки UNIQUE KEY.

Недостаток:Пользователь А извлекает идентификатор, который проверяется как еще не использованный. Затем код попытается вставить данные. Но тем временем Пользователь B вошел в тот же цикл и, к сожалению, получает то же случайное число, потому что Пользователь А еще не сохранен, и этот идентификатор все еще был свободен. Теперь система сохраняет либо Пользователь B, либо Пользователь А, и при попытке сохранить второго пользователя уже есть другой пользователь с тем же идентификатором.

Вам нужно будет обработать это исключение в любом случае и повторить попытку вставки с вновь созданным идентификатором. Добавление этого при сохранении пессимистичного цикла проверки (который вам потребуется повторно ввести) приведет к довольно уродливому и сложному для понимания коду. К счастью, решение этой проблемы такое же, как и решение проблемы: Просто сделайте это в первую очередь и попробуйте сохранить данные. В случае ошибки UNIQUE KEY просто повторите попытку с новым идентификатором.

Не нужно проверять базу данных, просто вставляйте / ловите / регенерируйте.

Roman Newaza 21.11.2013 12:54

Взгляните на эту статью

В нем объясняется, как генерировать короткие уникальные идентификаторы из ваших идентификаторов bdd, как это делает YouTube.

На самом деле функция в статье очень похожа на функция php base_convert, которая преобразует число из одного основания в другое (но только до 36).

Вы можете добиться этого с меньшим количеством кода:

function gen_uid($l=10){
    return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}

Результат (примеры):

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi

Отличное решение, но оно может возвращать только одно вхождение каждой буквы, что ограничивает возможности. Немного переписал: function gen_uid($l=10){ $str = ""; for ($x=0;$x<$l;$x++) $str .= substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, 1); return $str; }

LobsterMan 21.03.2017 11:56

Лучший ответ: Наименьшая уникальная строка типа «хэш-лайк» с уникальным идентификатором базы данных - решение PHP, сторонние библиотеки не требуются.

Вот код:

<?php
/*
THE FOLLOWING CODE WILL PRINT:
A database_id value of 200 maps to 5K
A database_id value of 1 maps to 1
A database_id value of 1987645 maps to 16LOD
*/
$database_id = 200;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 200 maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1 maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1987645 maps to $base36value\n";

// HERE'S THE FUNCTION THAT DOES THE HEAVY LIFTING...
function dec2string ($decimal, $base)
// convert a decimal number into a string using $base
{
    //DebugBreak();
   global $error;
   $string = null;

   $base = (int)$base;
   if ($base < 2 | $base > 36 | $base == 10) {
      echo 'BASE must be in the range 2-9 or 11-36';
      exit;
   } // if

   // maximum character string is 36 characters
   $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

   // strip off excess characters (anything beyond $base)
   $charset = substr($charset, 0, $base);

   if (!ereg('(^[0-9]{1,50}$)', trim($decimal))) {
      $error['dec_input'] = 'Value must be a positive integer with < 50 digits';
      return false;
   } // if

   do {
      // get remainder after dividing by BASE
      $remainder = bcmod($decimal, $base);

      $char      = substr($charset, $remainder, 1);   // get CHAR from array
      $string    = "$char$string";                    // prepend to output

      //$decimal   = ($decimal - $remainder) / $base;
      $decimal   = bcdiv(bcsub($decimal, $remainder), $base);

   } while ($decimal > 0);

   return $string;

}

?>

10 символов:

substr(uniqid(),-10);

5 двоичных символов:

hex2bin( substr(uniqid(),-10) );

8 символов base64:

base64_encode( hex2bin( substr(uniqid(),-10) ) );

Другие вопросы по теме