PHP « Генерация уникальных кодов

Нужно сделать генерацию уникальных кодов, что бы была 100% гарантия.
Сайт по продаже билетов, каждый билет - уникальный код.
Вот, думаю, как бы сделать. Может, кто-то уже делал, или просто знает, как лучше?
Спасибо!
UPD:
Желательно, что бы длина ключа была единой. 8 символов, допустим.

1 ответов


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

К примеру по типам событий:
CL12345 - префикс CL - клубный ключ
EV12345 - префикс EV - ключ для события(выставки)

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

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


CREATE TABLE `unique_keys` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `key_value` varchar(8) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `key_value` (`key_value`)
);
 

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

К примеру:


/* Функция генерации уникальной строки
 * @var length - длина генерируемой последовательности
 * Так как я в своем примере использую префикс в 2 символа,
 * то по молчанию длинна основного ключа: +6 символов
 * @var chars - набор символов, участвующих в генерации
 * в наборе может присутствовать что угодно:
 * $chars = 'ABCDEFGHJKLMNOPQRSTUVWXYZ1234567890'
 */


function rand_str($length = 6, $chars = '1234567890') {
    // получаем длину строки символов
    $chars_length = (strlen($chars) - 1);

    // Итак, строчка начинается
    $string = $chars{rand(0, $chars_length)};
   
    // Генерируем
    for ($i = 1; $i < $length; $i = strlen($string))  {
        // Берем случайный элемент из набора символов
        $r = $chars{rand(0, $chars_length)};
       
        // Убеждаемся, что соседние символы не совпадают.
        if ($r != $string{$i - 1}) $string .=  $r;
    }
   
    // Вуаля!
    return $string;
}

/*  функция добавления уникальной записи
 *  @var prefix - тот самый префикс, о котором я говорил,
 * естественно этого параметра можно избежать.
 */

function add_unique_key ($prefix = 'EV') {
  // Вбиваем уникальный ключ
   while (!mysql_query("INSERT INTO unique_keys (key_value) VALUES  ("
             . $prefix . rand_str() . ")")) {};
 
  // на выходе мы точно получим уникальную запись.
  return mysql_insert_id();
}
 

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

update:
И да, сюда еще было бы правильней дописать функцию форматированного вывода
ключа. Что бы "читабельность" еще более повысилась.


function getFormattedKey($key) {
 // Получаем "CL123456"
  list($prefix, $num1, $num2) = sscanf($key, "%s%d{3}%d{3}");
 // Возвращаем "CL-123-456"
  return $prefix . "-" . $num1 . "-" . $num2;
}
 


update №2:
Да, есть еще такой ньюанс - если нет "автоматизированной" системы проверки кодов, то для "защиты" используют некий алгоритм генерации кодов.
Самый простой пример - допустим, что сумма каждых следующих двух чисел - целое число. Такая "элементарная защита" от дурака.
Подобными способами защищаются, пожалуй чуть ли не 80% софтверных компаний, пытающихся уйти от злополучных кейгенделов.

У каждого билета новерное есть свой id, не так ли? Что если к этому id добавить любое случайно сгенерированное сочетание символов, которое даже может повторяться и из этой связки сделать хеш :?

В таком случае у нас может быть 13-dha0982e и 142-dha0982e, хоть случайные символы и совпали, то из-за id билета хеш будет различаться.

Добавил вопрос в закладки, самому интересно узнать наилучший вариант ;)


Почитайте про GUID.
А именно про uuid_create(), например.
Или можно написать свою псевдослучайную генерилку GUID'ов, например:


function getGuid(){
        mt_srand((double)microtime()*10000);
        $charid = strtoupper(md5(uniqid(rand(), true)));
        $hyphen = chr(45);
        $uuid = substr($charid, 0, 8).$hyphen
                .substr($charid, 8, 4).$hyphen
                .substr($charid,12, 4).$hyphen
                .substr($charid,16, 4).$hyphen
                .substr($charid,20,12);
    return $uuid;
}
 
На данном коде было сделано 10 000 000 GUID'ов и повторений при этом не было.