PHPの短い一意のID
-
08-07-2019 - |
質問
一意のIDを作成したいのですが、uniqid()
が'492607b0ee414'
のようなものを与えています。私が欲しいのは、tinyurlが提供するものに似たものです:'64k8ra'
。短いほど良い。唯一の要件は、明白な順序を持たせてはならず、一見ランダムに見える一連の数字よりもきれいに見えることです。文字は数字よりも優先され、理想的には大文字と小文字が混在しないようにします。エントリの数はそれほど多くないため(最大10000程度)、衝突のリスクは大きな要因ではありません。
ご意見をお寄せください。
解決
指定された長さのランダムな文字を返す小さな関数を作成します。
<?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')));
他のヒント
@gen_uuid()by gord。
preg_replaceに厄介なutf-8の問題が発生しました。これにより、uidには<!> quot; + <!> quot;が含まれることがあります。または<!> quot; / <!> quot;。 これを回避するには、パターン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
信頼性のある一意のIDを取得するには、2つの方法があります:衝突の可能性が(GUIDのように)極端に短くなるように長く変更するか、ルックアップのためにテーブルに生成されたすべてのIDを(メモリまたは生成時に一意性を検証するために、DBまたはファイルに保存します。
このような短いキーを生成し、何らかの重複チェックなしでその一意性を保証する方法を本当に求めている場合、答えはできません。
これは、任意の長さのランダム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);
}
本当にシンプルなソリューション:
一意のIDを作成するには:
$id = 100;
base_convert($id, 10, 36);
元の値を再度取得します:
intval($str,36);
これは別のスタックオーバーフローページからのものであるため、信用できませんが、このソリューションは非常にエレガントで素晴らしいものであるため、このスレッドを参照する人々にとってこのスレッドにコピーする価値があると思いました。
IDを使用して、前後に変換する場合は、base-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の例でそれを理解できるでしょう。この不明瞭さがセキュリティに取って代わらないようにしてください。
一意性チェックなしでこれを行う非常にクールなソリューションだと思うものを思いつきました。将来の訪問者と共有したいと思いました。
カウンターは、一意性を保証するための非常に簡単な方法です。データベースを使用している場合、主キーも一意性を保証します。問題は、見た目が悪く、脆弱である可能性があることです。それで私はシーケンスを取り、暗号でそれをごちゃ混ぜにしました。暗号は逆にすることができるため、各IDは一意でありながらランダムに表示されます。
これはphpではなくpythonですが、ここにコードをアップロードしました: https://github.com/adecker89/Tiny-Unique-Identifiers
文字はきれいで、数字はいです。 ランダムな文字列が必要ですが、<!> quot; ugly <!> quot;は必要ありません。ランダムな文字列?
乱数を作成し、予約<!> quot; numbers <!> quot;のように alpha-style ( base-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');
}
このようにすることもできます:
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);
}
文字列を正確な長さにする必要がある場合は、16進数にゼロを追加するだけです:
function createRandomString($length)
{
$random = mt_rand(0, (1 << ($length << 2)) - 1);
$number = dechex($random);
return str_pad($number, $length, '0', STR_PAD_LEFT);
}
<!> quot;理論的なバックドロー<!> quot; PHPの機能に制限されているということです-しかし、これはその場合の哲学的な問題です;)とにかくそれを見ていきましょう:
- PHPは、このように16進数で表現できるものに制限があります。これは32ビットシステムでは
$length <= 8
少なくともで、PHPの制限は4.294.967.295です。 - PHPの乱数ジェネレーターにも最大値があります。 32ビットシステムの
do { (generate ID) } while { (id is not uniqe) } (insert id)
少なくともの場合、2.147.483.647 である必要があります
- したがって、理論的には2.147.483.647 IDに制限されます。
トピックに戻る-直観的な<=>には、1つの欠点と1つの考えられる欠陥があります。
欠点:検証は悲観的です。このように行うには、常に常にデータベースをチェックする必要があります。十分なキースペース(たとえば、10,000エントリの長さ5)があると、データを保存して再試行するだけのリソース消費が比較的少なくなるため、衝突が頻繁に発生することはほとんどありませんユニークキーエラー。
欠陥: ユーザーA は、まだ取得されていないことが確認されるIDを取得します。次に、コードはデータを挿入しようとします。ただし、その間、ユーザーB は同じループに入り、残念ながら同じ乱数を取得します。これは、ユーザーA がまだ保存されておらず、このIDがまだ空いているためです。システムは User B または User A のいずれかを保存します。2番目のユーザーを保存しようとすると、その間にもう1人が同じIDを持ちます。
いずれにしても、その例外を処理する必要があり、新しく作成されたIDで挿入を再試行する必要があります。悲観的なチェックループ(再入力が必要)を維持しながらこれを追加すると、コードが非常に見づらくなりにくくなります。幸いなことに、これに対する解決策は、欠点に対する解決策と同じです。そもそもそれを探して、データを保存してみてください。 UNIQUE KEYエラーの場合は、新しいIDで再試行してください。
この記事をご覧ください
YouTubeのように、bdd IDから短い一意のIDを生成する方法を説明します。
実際、この記事の関数は 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の長いバージョンが必要な場合は、これを使用します:
$ uniqueid = sha1(md5(time()));
ベストアンサー:一意のデータベース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;
}
?>