PHP короткий хэш, как URL-сокращение веб-сайтов
Я ищу функцию PHP, которая создает короткий хэш из строки или файла, подобный тем веб-сайтам сокращения URL, таким как tinyurl.com
хэш не должен быть длиннее 8 символов.
8 ответов:
службы сокращения URL-адресов вместо этого используют автоматически увеличенное целочисленное значение (например, дополнительный идентификатор базы данных) и кодируют его с помощью Base64 или другие кодировки, чтобы иметь больше информации на символ (64 вместо 10, как цифры).
TinyURL ничего не хэширует, он использует целые числа Base 36 (или даже base 62, используя нижние и верхние буквы), чтобы указать, какую запись посетить.
база 36 в целое число:
intval($str, 36);целое число к основанию 36:
base_convert($val, 10, 36);Итак, вместо перенаправления на маршрут как
/url/1234становится . Это дает вам намного больше пользы, чем хэш, так как не будет никаких столкновений. С помощью этого вы можете легко проверить, существует ли url-адрес и вернуться правильный, существующий идентификатор в базе 36 без того, чтобы пользователь знал, что он уже был в базе данных.не хэш, используйте другие базы для такого рода вещей. (Это быстрее и может быть сделано столкновение доказательство.)
Я написал крошечную lib для генерации запутанных хэшей из целых чисел.
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 6hqqy14.07.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; } }
самый короткий хэш-это длина 32 символа, как вы можете использовать первые 8 символов хэша md5
echo substr(md5('http://www.google.com'), 0, 8);обновление: вот еще один класс, нашел здесь написано Экскурсия Перкинс который принимает номер записи и создать короткий хэш для него. 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
вот код:
<?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/......
где статус указывает, является ли этот хэш свободным или нет.
простой способ с дублированием проверки в базе данных:
$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; } }
Я находить похожие изображения URL-адрес. В моем случае я использовал " id " базы данных для создания каждый раз уникального короткого url.
то, что я сделал, во-первых -
вставьте данные, такие как" исходный url "и" дата создания "в БД, оставив" короткий url " пустым в БД. Затем получите " 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>"; ?>а затем обновить значение короткого Url-кода в базе данных.
здесь я использую " id " для создания короткого кода. Поскольку ID не может быть одинаковым для нескольких записей. Это уникальный, следовательно, уникальный код или Url будет уникальным.
Comments