PHP короткий хаш, как URL-сокращение сайтов

Я ищу функцию PHP, которая создает короткий хэш из строки или файла, похожего на те сайты, которые сокращают URL-адрес, например tinyurl.com

Хэш не должен превышать 8 символов.

Устранители URL-адресов скорее используют автоматическое добавочное целочисленное значение (например, дополнительный идентификатор базы данных) и кодируют его с помощью Base64 или других кодировок, чтобы иметь больше информации на символ (64 вместо десяти цифр).

TinyURL ничего не делает, он использует базовые 36 целых чисел (или даже базу 62, используя буквы нижнего и верхнего регистра), чтобы указать, какую запись нужно посетить.

База 36 для целого:

 intval($str,36) 

Целое число к базе 36:

 base_convert($val, 10, 36) 

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

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

Я написал крошечную библиотеку для создания запутанных хэшей из целых чисел.

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: добавление фактического кода ниже, так как стало трудно найти:

 <?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, AZ, az */ /* $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; } } 

Самый короткий хэш – 32 символа, как только вы можете использовать первые 8 символов хеша md5

 echo substr(md5('http://www.google.com'), 0, 8); 

Обновление : вот еще один класс, найденный здесь, написанный Travell Perkins, который записывает номер и создает короткий хэш для него. 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))); } } 

вот пример того, как его использовать:

 BaseIntEncoder::encode('1122344523');//result:3IcjVE BaseIntEncoder::decode('3IcjVE');//result:1122344523 

Лучший ответ: наименьшая уникальная строка «Hash Like» с уникальным идентификатором базы данных – решение для 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 $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; } ?> 

На самом деле лучшим решением для «случайного» хэша является генерация списка случайных хэшей, размещение его на Mysql с уникальным индексом (вы можете написать простой UDF для вставки 100 000 строк за 1 секунду).

Я думаю, что структура вроде этого ID | HASH | STATUS | URL | VIEWS | ……

Если статус указывает, свободен ли этот хэш или нет.