web-dev-qa-db-ja.com

PHPの短い一意のID

一意のIDを作成したいが、uniqid()'492607b0ee414'。私が欲しいのは、tinyurlが提供するものに似たものです:'64k8ra'。短いほど良い。唯一の要件は、明白な順序を持たないことと、一見ランダムな数字のシーケンスよりきれいに見えることです。文字は数字よりも優先され、大文字と小文字が混在しないことが理想的です。エントリの数はそれほど多くないため(最大10000程度)、衝突のリスクは大きな要因ではありません。

どんな提案も感謝します。

46
Antti

指定された長さのランダムな文字を返す小さな関数を作成します。

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

また、文字が辞書の単語を形成しないようにすることもできます。顧客が不快なものを見つけることを避けるために、それは英語の辞書全体であってもよいし、単に悪い単語の辞書であってもよい。

編集:これを追加するのは、あなたがそれを使用するつもりで、大量のアイテムではない場合にのみ意味があります。これは、衝突が増えるとかなり遅くなる可能性があるためです(すでにテーブルにIDを取得しています)。もちろん、インデックス付きのテーブルが必要であり、衝突を避けるためにIDの文字数を微調整する必要があります。この場合、6文字の場合、26 ^ 6 = 308915776の一意のID(悪い単語を除く)があり、10000の必要性に十分です。

編集:文字と数字の組み合わせが必要な場合は、次のコードを使用できます。

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

@gen_uuid()by 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);
}

それを見つけるのにかなり時間がかかりました。おそらく他の人の頭痛の種を救うでしょう

28
Corelgott

より少ないコードでそれを達成できます:

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

結果(例):

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi
24
Nico Schefer

確実に一意のIDを取得するには、2つの方法があります。衝突の可能性が(GUIDのように)非常に小さく可変になるようにするか、ルックアップのためにテーブルに生成されたすべてのIDを保存します(メモリまたはDBに)またはファイル)を生成時に一意性を検証します。

このような短いキーをどのように生成し、何らかの重複チェックなしでその一意性を保証する方法を本当に求めているのであれば、答えはできません。

17
Chris

これは、任意の長さのランダムbase62に使用するルーチンです...

gen_uuid()を呼び出すと、WJX0u0jV, E9EMaZ3Pなど.

デフォルトでは、これは8桁を返します。したがって、64 ^ 8または約10 ^ 14のスペースであり、これは衝突を非常にまれにするのに十分です。

大きいまたは小さい文字列の場合、必要に応じて$ lenを渡します。満足するまで追加するため、長さの制限はありません(最大128文字の安全制限まで、削除可能)。

ランダムなソルトinsidemd5 [または必要に応じてsha1]を使用するので、簡単にリバースエンジニアリングできないことに注意してください。

Webで信頼できるbase62変換が見つからなかったため、base64の結果から文字を削除するこのアプローチ。

BSDライセンスの下で自由に使用し、楽しんで、

gord

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

本当に簡単なソリューション:

以下を使用して一意のIDを作成します。

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

元の値を再度取得します。

intval($str,36);

別のスタックオーバーフローページからのものであるため、これは信用できませんが、ソリューションは非常にエレガントで素晴らしいと思い、これを参照する人々のためにこのスレッドにコピーする価値があると思いました。

11
Adcuz

私は、一意性チェックなしでこれを行う非常にクールなソリューションだと思うものを思いつきました。将来の訪問者と共有したいと思いました。

カウンターは、一意性を保証する非常に簡単な方法です。データベースを使用している場合、主キーも一意性を保証します。問題は、見た目が悪く、脆弱である可能性があることです。それで、私はシーケンスを取り、暗号でそれをごちゃ混ぜにしました。暗号は元に戻すことができるため、各IDは一意でありながらランダムに表示されます。

python phpではありませんが、ここにコードをアップロードしました: https://github.com/adecker89/Tiny-Unique-Identifiers

4
AJD

前後に変換する場合は、IDを使用して、36進数に変換できます。整数IDを持つ任意のテーブルに使用できます。

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の例でそれを理解できるでしょう。この不明瞭さがセキュリティに取って代わらないようにしてください。

4
OIS

文字はきれいで、数字はいです。ランダムな文字列が必要ですが、「ugい」ランダムな文字列は必要ありませんか?

航空会社が提供する予約「番号」のように、乱数を作成し、alpha-stylebase-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
RJHunter

あなたはそれをwithoutなしで行うことができます。きれいで読みやすい方法で、ループ、文字列連結、Rand()への複数の呼び出しのような汚れた/高価なもの。また、mt_Rand()を使用することをお勧めします。

_function createRandomString($length)
{
    $random = mt_Rand(0, (1 << ($length << 2)) - 1);
    return dechex($random);
}
_

いずれにしても文字列の正確な長さが必要な場合は、16進数にゼロを追加するだけです。

_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は、このように16進数として表現できるものに制限があります。これは32ビットシステムでは_$length <= 8_少なくともになります。この場合のPHPの制限は4.294.967.295です。
  • PHPの乱数ジェネレーターにも最大値があります。 32ビットシステムのmt_Rand()少なくともの場合、2.147.483.647である必要があります
  • したがって、理論的には2.147.483.647 IDに制限されます。

トピックに戻って-直感的なdo { (generate ID) } while { (id is not uniqe) } (insert id)には1つの欠点と1つの考えられる欠陥があります。

欠点:検証は悲観的です。このようにalways行うには、データベースでのチェックが必要です。十分なキースペース(たとえば、10,000エントリの長さ5)があると、比較的リソース消費が少なくなるため、衝突が頻繁に発生することはほとんどありませんUNIQUE KEYエラーの場合にのみ、データを保存して再試行してください。

欠陥:ユーザーAは、まだ取得されていないことが検証されるIDを取得します。次に、コードはデータを挿入しようとします。しかし、その間、ユーザーBは同じループに入り、残念ながらユーザーAはまだ保存されておらず、このIDはまだ空いています。システムは、ユーザーBまたはユーザーAのいずれかを保存し、 2番目のユーザーを保存するために、もう一方のユーザーが同じIDを持っています。

いずれにせよその例外を処理する必要があり、新しく作成されたIDで挿入を再試行する必要があります。悲観的なチェックループ(再入力が必要)を保持しながらこれを追加すると、コードが非常にくなり、追跡が難しくなります。 幸いなことに、これに対する解決策は欠点の解決策と同じです:そもそもそれを選んでデータを保存してみてください。 UNIQUE KEYエラーの場合は、新しいIDで再試行してください。

1
nico gawenda

この記事をご覧ください

Youtubeのように、bdd IDから短い一意のIDを生成する方法を説明します。

実際、記事の関数は php function base_convert に非常に関連しています。これは、数値を基数から別の基数に変換します(ただし、基数は36までです)。

1
httpete

このようにすることもできます:

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;
    }
1
Aldee

一意のIDの長いバージョンが必要な場合は、これを使用します。
$ uniqueid = sha1(md5(time()));

0
Alon Kogan

まだベストアンサー: 一意のデータベースIDを指定した最小の一意の「ハッシュのような」文字列-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;

}

?>
0
John Erck