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

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

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

15 ответов


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

<?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);

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

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

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

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

@gen_uuid () по gord.

preg_replace получил некоторые неприятные проблемы utf-8, что заставляет uid somtimes содержать "+" или "/". Чтобы обойти это, вы должны явно сделать шаблон 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);
}

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


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

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

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

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi

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

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


очень простое решение:

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

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

получить исходное значение:

intval($str,36);

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


вот рутины я использую для случайных base62s любой длины...

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

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

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

Примечание, используйте случайный соль!--12-->внутри 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);
}

вы можете использовать Id и просто преобразовать его в номер base-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

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


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

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

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


буквы красивые, цифры некрасивые. Вы хотите случайные строки, но не хотите" уродливые " случайные строки?

создать случайное число и напечатать его в Альфа-стиль (база-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');
}

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

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);
}

"теоретический backdraw" заключается в том, что вы ограничены возможностями PHPs , но в этом случае это скорее философский вопрос;) пойдем через него все равно:

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

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

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

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

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


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

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

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


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);
}

Если вам нравится более длинная версия уникального Id, используйте это:
$уникальный идентификатор = значение SHA1(MD5 в(время()));


Лучший Ответ: наименьшая уникальная строка" Hash Like " с учетом уникального идентификатора базы данных-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;

}

?>