PHP 短哈希,如 URL 缩短网站
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/959957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP short hash like URL-shortening websites
提问by clamp
I am looking for a PHP function that creates a short hash out of a string or a file, similar to those URL-shortening websites like tinyurl.com
我正在寻找一个 PHP 函数,它可以从字符串或文件中创建一个简短的哈希值,类似于像tinyurl.com这样的 URL 缩短网站
The hash should not be longer than 8 characters.
散列不应超过 8 个字符。
采纳答案by Gumbo
URL shortening services rather use a auto incremented integer value (like a supplementary database ID) and encode that with Base64or other encodings to have more information per character (64 instead of just 10 like digits).
URL 缩短服务而是使用自动递增的整数值(如补充数据库 ID)并使用Base64或其他编码对其进行编码,以便每个字符具有更多信息(64 而不是像数字一样只有 10 个)。
回答by Robert K
TinyURL doesn't hash anything, it uses Base 36 integers (or even base 62, using lower and uppercase letters) to indicate which record to visit.
TinyURL 不散列任何内容,它使用 Base 36 整数(甚至 Base 62,使用小写和大写字母)来指示要访问的记录。
Base 36 to Integer:
基数 36 到整数:
intval($str, 36);
Integer to Base 36:
整数到基数 36:
base_convert($val, 10, 36);
So then, instead of redirecting to a route like /url/1234it becomes /url/axinstead. This gives you a whole lot more use than a hash will, as there will be no collisions. With this you can easily check if a url exists and return the proper, existing, ID in base 36 without the user knowing that it was already in the database.
那么,而不是像/url/1234它那样重定向到路由/url/ax。这为您提供了比散列更多的用途,因为不会发生冲突。有了这个,您可以轻松地检查 url 是否存在,并以 base 36 返回正确的现有 ID,而用户不知道它已经在数据库中。
Don't hash, use other bases for this kind of thing. (It's faster and can be made collision-proof.)
不要散列,这种事情使用其他基础。(它更快,并且可以防碰撞。)
回答by KevBurnsJr
I wrote a tiny lib to generate obfuscated hashes from integers.
我写了一个小库来从整数生成混淆哈希。
http://web.archive.org/web/20130727034425/http://blog.kevburnsjr.com/php-unique-hash
http://web.archive.org/web/20130727034425/http://blog.kevburnsjr.com/php-unique-hash
$ids = range(1,10);
foreach($ids as $id) {
echo PseudoCrypt::unhash($id) . "\n";
}
m8z2p 8hy5e uqx83 gzwas 38vdh phug6 bqtiv xzslk k8ro9 6hqqy
7/14/2015: Adding the actual code below, since it has become difficult to find:
7/14/2015:添加下面的实际代码,因为它变得很难找到:
<?php
/**
* PseudoCrypt by KevBurns (http://blog.kevburnsjr.com/php-unique-hash)
* Reference/source: http://stackoverflow.com/a/1464155/933782
*
* I want a short alphanumeric hash that's unique and who's sequence is difficult to deduce.
* I could run it out to md5 and trim the first n chars but that's not going to be very unique.
* Storing a truncated checksum in a unique field means that the frequency of collisions will increase
* geometrically as the number of unique keys for a base 62 encoded integer approaches 62^n.
* I'd rather do it right than code myself a timebomb. So I came up with this.
*
* Sample Code:
*
* echo "<pre>";
* foreach(range(1, 10) as $n) {
* echo $n." - ";
* $hash = PseudoCrypt::hash($n, 6);
* echo $hash." - ";
* echo PseudoCrypt::unhash($hash)."<br/>";
* }
*
* Sample Results:
* 1 - cJinsP - 1
* 2 - EdRbko - 2
* 3 - qxAPdD - 3
* 4 - TGtDVc - 4
* 5 - 5ac1O1 - 5
* 6 - huKpGQ - 6
* 7 - KE3d8p - 7
* 8 - wXmR1E - 8
* 9 - YrVEtd - 9
* 10 - BBE2m2 - 10
*/
class PseudoCrypt {
/* Key: Next prime greater than 62 ^ n / 1.618033988749894848 */
/* Value: modular multiplicative inverse */
private static $golden_primes = array(
'1' => '1',
'41' => '59',
'2377' => '1677',
'147299' => '187507',
'9132313' => '5952585',
'566201239' => '643566407',
'35104476161' => '22071637057',
'2176477521929' => '294289236153',
'134941606358731' => '88879354792675',
'8366379594239857' => '7275288500431249',
'518715534842869223' => '280042546585394647'
);
/* Ascii : 0 9, A Z, a z */
/* $chars = array_merge(range(48,57), range(65,90), range(97,122)) */
private static $chars62 = array(
0=>48,1=>49,2=>50,3=>51,4=>52,5=>53,6=>54,7=>55,8=>56,9=>57,10=>65,
11=>66,12=>67,13=>68,14=>69,15=>70,16=>71,17=>72,18=>73,19=>74,20=>75,
21=>76,22=>77,23=>78,24=>79,25=>80,26=>81,27=>82,28=>83,29=>84,30=>85,
31=>86,32=>87,33=>88,34=>89,35=>90,36=>97,37=>98,38=>99,39=>100,40=>101,
41=>102,42=>103,43=>104,44=>105,45=>106,46=>107,47=>108,48=>109,49=>110,
50=>111,51=>112,52=>113,53=>114,54=>115,55=>116,56=>117,57=>118,58=>119,
59=>120,60=>121,61=>122
);
public static function base62($int) {
$key = "";
while(bccomp($int, 0) > 0) {
$mod = bcmod($int, 62);
$key .= chr(self::$chars62[$mod]);
$int = bcdiv($int, 62);
}
return strrev($key);
}
public static function hash($num, $len = 5) {
$ceil = bcpow(62, $len);
$primes = array_keys(self::$golden_primes);
$prime = $primes[$len];
$dec = bcmod(bcmul($num, $prime), $ceil);
$hash = self::base62($dec);
return str_pad($hash, $len, "0", STR_PAD_LEFT);
}
public static function unbase62($key) {
$int = 0;
foreach(str_split(strrev($key)) as $i => $char) {
$dec = array_search(ord($char), self::$chars62);
$int = bcadd(bcmul($dec, bcpow(62, $i)), $int);
}
return $int;
}
public static function unhash($hash) {
$len = strlen($hash);
$ceil = bcpow(62, $len);
$mmiprimes = array_values(self::$golden_primes);
$mmi = $mmiprimes[$len];
$num = self::unbase62($hash);
$dec = bcmod(bcmul($num, $mmi), $ceil);
return $dec;
}
}
回答by Nazariy
Shortest hash is 32 character length, how ever you can use first 8 characters of md5 hash
最短的哈希是 32 个字符的长度,你怎么能使用 md5 哈希的前 8 个字符
echo substr(md5('http://www.google.com'), 0, 8);
Update: here is another class found herewritten by Travell Perkinswhich takes record number and create short hash for it. 14 digits number produce 8 digit string. By the date you reach this number you become more popular than tinyurl ;)
更新:这里是另一个类中找到这里书面特拉维尔帕金斯这需要记录号码并为它创建短哈希。14 位数字产生 8 位字符串。当你达到这个数字时,你变得比 tinyurl 更受欢迎;)
class BaseIntEncoder {
//const $codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
//readable character set excluded (0,O,1,l)
const codeset = "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ";
static function encode($n){
$base = strlen(self::codeset);
$converted = '';
while ($n > 0) {
$converted = substr(self::codeset, bcmod($n,$base), 1) . $converted;
$n = self::bcFloor(bcdiv($n, $base));
}
return $converted ;
}
static function decode($code){
$base = strlen(self::codeset);
$c = '0';
for ($i = strlen($code); $i; $i--) {
$c = bcadd($c,bcmul(strpos(self::codeset, substr($code, (-1 * ( $i - strlen($code) )),1))
,bcpow($base,$i-1)));
}
return bcmul($c, 1, 0);
}
static private function bcFloor($x)
{
return bcmul($x, '1', 0);
}
static private function bcCeil($x)
{
$floor = bcFloor($x);
return bcadd($floor, ceil(bcsub($x, $floor)));
}
static private function bcRound($x)
{
$floor = bcFloor($x);
return bcadd($floor, round(bcsub($x, $floor)));
}
}
here is example how to use it:
这是如何使用它的示例:
BaseIntEncoder::encode('1122344523');//result:3IcjVE
BaseIntEncoder::decode('3IcjVE');//result:1122344523
回答by John Erck
Best Answer Yet: Smallest Unique "Hash Like" String Given Unique Database ID - PHP Solution, No Third Party Libraries Required.
最佳答案:给定唯一数据库 ID 的最小唯一“类哈希”字符串 - PHP 解决方案,不需要第三方库。
Here's the code:
这是代码:
<?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 $database_id maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of $database_id maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of $database_id 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;
}
?>
回答by Gediminas
Easy way with duplicate checking in Database:
在数据库中进行重复检查的简单方法:
$unique = false;
// While will be repeated until we get unique hash
while($unique == false) {
// Getting full hash based on random numbers
$full_hash = base64_encode( rand(9999,999999) );
// Taking only first 8 symbols
$hash = substr($full_hash, 0, 8);
// Checking for duplicate in Database - Laravel SQL syntax
$duplicate = \App\Item::where('url', $hash)->count();
// If no Duplicate, setting Hash as unique
if ($duplicate==0) {
// For stoping while
$unique=true;
// New Hash is confirmed as unique
$input['url']=$hash;
}
}
回答by lazycipher
I was making a url shortner. In my case I used the "id" of database to create every time a unique short url.
我正在制作一个网址缩短器。就我而言,我每次都使用数据库的“id”来创建一个唯一的短网址。
What I did is, first -
我所做的是,首先——
Insert Data like "Original url" and "creation date" in db leaving the "short url" empty in db. Then get the "id" from there and pass in the function below.
在数据库中插入“原始网址”和“创建日期”等数据,将数据库中的“短网址”留空。然后从那里获取“id”并传入下面的函数。
<?php
function genUniqueCode($id){
$id = $id + 100000000000;
return base_convert($id, 10, 36);
}
//Get Unique Code using ID
/*
id Below is retrived from Database after Inserting Original URL.
*/
$data['id'] =10;
$uniqueCode = genUniqueCode($data['id']);
// Generating the URL
$protocol = strtolower(substr($_SERVER["SERVER_PROTOCOL"],0,5))=='https'?'https':'http';
echo "<a href='{$protocol}://{$_SERVER['HTTP_HOST']}/{$uniqueCode}'>{$protocol}://{$_SERVER['HTTP_HOST']}/{$uniqueCode}</a>";
?>
And then UPDATE value of Short Url Code in Database.
然后更新数据库中短网址代码的值。
Here I'm using "id" to create short code. Since ID can't be same for multi entries. It's unique hence the Unique Code or Url will be unique.
在这里,我使用“id”来创建短代码。由于多个条目的 ID 不能相同。它是唯一的,因此唯一代码或 URL 将是唯一的。
回答by Thomas Decaux
Actually the best solution to have "random" hash is to generate a list of random hash, put it on Mysql with an unique INDEX (you can write a simple UDF to insert 100 000 rows in 1 seconde).
实际上,拥有“随机”散列的最佳解决方案是生成一个随机散列列表,将其放在具有唯一 INDEX 的 Mysql 上(您可以编写一个简单的 UDF 以在 1 秒内插入 100 000 行)。
I think a structure like this ID|HASH|STATUS|URL|VIEWS|......
我认为像这样的结构 ID|HASH|STATUS|URL|VIEWS|......
Where status indicates if this Hash is free or not.
其中 status 指示此 Hash 是否空闲。

