2015-04-30 3 views
3

У меня есть интересный пример, когда несколько повторных вызовов uniqid() не генерируют уникальные номера при локальном размещении на XAMPP. Уникальный идентификатор повторяется где-то между 5-20 раз и затем таинственно изменяется.несколько вызовов uniqid() не являются уникальными

Однако, как интересный поворот, код работает отлично на нашем производственном сервере.

Итак, вот что я делаю: Я создаю оболочку, которая при щелчке скрывает/скрывает дочерний контент в div через простую функцию javascript. Поскольку скрываемый div генерируется динамически, на него ссылается уникальный идентификатор, который генерируется PHP.

Пример вопроса заключается в следующем:

// Replace something like '[element] => <newline> (' with <a href="javascript:toggleDisplay('[unique id]');">...</a><div id="[unique id]" style="display: none;"> 
$out = preg_replace_callback(
     $regex, 
     function ($matches) { 
      $id = uniqid(); 
      return $matches[1] . "<a class='debug' href='javascript:toggleDisplay(\"" . $id . "\");'>" . $matches[2] . "</a>" . "<div id='" . $id . "' style='display: none'>"; 
     }, $out 
    ); 

Javascript функция выглядит следующим образом (только так вы можете увидеть, что я делаю, это работает отлично):

<script language="Javascript"> 
    function toggleDisplay(id) { 
     document.getElementById(id).style.display = (document.getElementById(id).style.display == "block") ? "none" : "block"; 
    } 
</script>' 

Проблема в том, что выходные divs имеют одинаковый уникальный идентификатор (!!), в кластерах где-то между 5-15, поэтому javascript не знает, какой div ссылаться.

Таким образом, некоторые вещи, которые я нашел: Если я что-то вроде $id = uniqid() . rand(10000,99999) вместо того, чтобы просто $id = uniqid() то код снова работает как задумано. Поэтому я уверен, что проблема в том, что uniqid() фактически не генерирует уникальный идентификатор, учитывая, что я не переписываю или повторно использую переменную $id.

Еще одна интересная вещь, которую я нашел: если я выводящая ее microtime() вместе с uniqid(), то uniqid() изменяет только, когда microtime() изменения. Для меня это похоже на подсказку.

Так что мой вопрос: Почему бы uniqid() только иногда генерировать uniqid()? Разве не uniqid() должен генерировать уникальный номер, даже если microtime() - это то же самое? Является ли это поведение документированным или хорошо известным? Или есть что-то еще, что мне не хватает?

Я прошу, потому что я чувствую себя некомфортно, используя uniqid(), потому что я не понимаю поведение ядра.

Любое понимание будет оценено по достоинству. Спасибо.

+0

На странице документации для 'uniqid' есть красный текст, в котором говорится, что он не генерирует случайные или непредсказуемые строки. Параметры для его создания используются с помощью 'openssl_random_pseudo_bytes', используя счетчик, созданный путем размещения исключительной блокировки файла, который содержит целое число, которое вы увеличиваете (эмулируете MySQL' auto_increment') или генерируете уникальный номер (снова auto_increment) через javascript, используя MySQL для получения UUID через 'SELECT UUID()' и т. д. –

+1

Также в документах для 'uniqid' есть необязательный второй параметр для« большей энтропии », который вы можете проверить.'uniqid (" ", true);' –

+1

Я не думаю, что он ожидает, что они будут случайными или непредсказуемыми. Просто, чтобы не было дубликатов. – Barmar

ответ

9

Результат uniqid() не гарантировано быть уникальным, и ваше расследование с microtime() действительно ключ к пониманию того, почему.

Согласно the manual page for uniqid(), это:

Получает префикс уникальный идентификатор на основе текущего времени в микросекундах.

Таким образом, основной ввод действительно является текущим «микрочеловеком». Тем не менее, он также имеет дополнительный параметр:

more_entropy Если установлено значение TRUE, uniqid() добавит дополнительную энтропию (с использованием комбинированного линейного конгруэнтного генератора) в конце возвращаемого значения, что увеличивает вероятность того, что результат будет уникальным.

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

Чтобы подтвердить, мы можем посмотреть на the source code for the function, где мы видим, что выход без набора more_entropy действительно представляет собой шестнадцатеричное представление текущей временной метки микросекунды. Интересный кусок, чтобы заметить это:

#if HAVE_USLEEP && !defined(PHP_WIN32) 
    if (!more_entropy) { 
#if defined(__CYGWIN__) 
     php_error_docref(NULL, E_WARNING, "You must use 'more entropy' under CYGWIN"); 
     RETURN_FALSE; 
#else 
     usleep(1); 
#endif 
    } 
#endif 

Так что, если вы не под Windows, функция будет фактически пытаться спать микросекунды, чтобы заставить последующие значения быть разными.

Это плохой способ запускать uniqid() много раз подряд, потому что, если он действительно преуспеет, он будет делать это медленно. (Требуется либо микросекунда сна, либо вызов генератора случайных чисел.)

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

$id_prefix = uniqid(); 
$id_suffix = 0; 
$out = preg_replace_callback(
     $regex, 
     function ($matches) use ($id_prefix, &$id_suffix) { 
      $id = $id_prefix . $id_suffix; 
      $id_suffix ++; 
      return $matches[1] . '... some html ...' . $id . ' ... '; 
     }, 
     $out 
); 
+0

А, это предложение «не под Windows» объясняет, почему я не смог воспроизвести его на своем Mac. – Barmar

+0

Это очень проницательный ответ, поскольку он затрагивает то, что делает «more_entropy» (и потенциальные проблемы с его использованием). Спасибо. –

+1

Ссылка на исходный код для тех, кто интересуется всем исходным кодом, теперь мертв, вместо этого используйте эту ссылку: https://github.com/php/php-src/blob/master/ext/standard/ uniqid.c –

2

Из документации uniqid()

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

Если ОС, на которой выполняется сценарий, не обеспечивает очень хорошее разрешение часов, то uniqid() может возвращать одно и то же значение несколько раз подряд.

Возможно, вы также использовали $id = uniqid(rand(10000,99999)), или для дальнейшего увеличения шансов случайности: $id = uniqid(rand(10000,99999), true).

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

Смежные вопросы