Короткий хеш PHP, як веб-сайти, що скорочують URL-адреси


83

Я шукаю функцію PHP, яка створює короткий хеш із рядка або файлу, подібну до тих веб-сайтів, що скорочують URL-адреси, таких як tinyurl.com

Хеш не повинен перевищувати 8 символів.


2
Я знаю, що це старе запитання, але перевірте: hashids.org . Працює з більшістю мов програмування
Грег

Перевірте бібліотеку ShortCode . Він робить саме те, що ви хочете. На основі базового перетворення.
Аніс,

Крім використання Adler-32 або CRC32, ви не можете укорочуєте сучасні (зіткнення стійкості) хеш , що багато (тобто до 8 символів). Ні з SHA-2, ні з SHA-1, ні навіть з MD5. За допомогою Alphabet::convert($hash, Alphabet::HEX, Alphabet::ALPHANUMERIC)ви можете отримати MD5 до 22 (з 32) символів. Натомість потрібно кодувати цілі ідентифікатори файлів (наприклад, з вашої бази даних) за допомогою (new Id())->encode($id).
каркати

Відповіді:


47

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


1
Що це означає (більше інформації на персонажа) просто цікаво !!
ravisoni

2
@ravisoni Якщо ви використовуєте десяткові цифри 0- 9для представлення числа, у вас є 10 можливих значень на кодований символ (ld (10) ≈ 3,32 біта / символ). Однак, якщо ви представляєте одне і те ж число з символами Base64, у вас буде 64 можливих значення на кодований символ (ld (64) = 6 біт / символ). Отже, у Base64 більше інформації зберігається в кодованому символі, тобто 6 бітів інформації замість 3,32 біта.
Гамбо

3
Якщо ви використовуєте base64, то ніщо не заважає сценарію вимовляти ($ i = 0; $ i <999999; $ i ++) {$ pageContent = fread (fopen (' yoururl.com/'.base64_encode($i) );} і тепер я маю доступ до кожної окремої URL-адреси у вашій базі даних

161

TinyURL нічого не хеш, він використовує цілі числа Base 36 (або навіть base 62, використовуючи малі та великі літери), щоб вказати, який запис відвідати.

База 36 до цілого числа:

intval($str, 36);

Ціле до бази 36:

base_convert($val, 10, 36);

Тоді, замість того, щоб перенаправляти на такий маршрут, яким /url/1234він стає /url/axнатомість. Це дає вам набагато більше користі, ніж хеш, оскільки зіткнень не буде. За допомогою цього ви можете легко перевірити, чи існує URL-адреса, і повернути правильний, існуючий ідентифікатор в базі 36, не знаючи, що користувач вже був у базі даних.

Не хеш, використовуйте інші основи для такого роду речей. (Це швидше, і його можна зробити стійким до зіткнень.)


привіт @RobertK, як PHP шукатиме для перетворення 6-значних рядків, які включають як цифри, так і букви?
Тім Петерсон

@timpeterson, просто зателефонуйте intval і передайте задану базу (див. мій перший блок коду).
Robert K

@RobertK, але intval()перетворює все на число. Я думаю, можливо, я збентежений тим, як intval()зв’язується з іншими кроками, необхідними для перенаправлення, як роль бази даних.
tim peterson

@timpeterson, це тому, що рядок представляє ідентифікатор запису бази даних. Отже, ви вибираєте запис на основі переданого ідентифікатора.
Robert K

@RobertK, проблема, з якою я стикаюся, полягає в тому intval(), чи є у вас $strпохилі риски (/) або тире (-). Я зрозумів це on/stuff, on-stuffі onвсі повернули номер 887. Ви маєте на увазі рішення для роботи з URL-адресами, де в них є коса риса?
Тім Петерсон

83

Я написав крихітну бібліотеку, щоб генерувати незрозумілі хеші з цілих чисел.

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

14.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;
    }

}

12
це має дуже розумний дизайн = D золотих штрихи = world.rock ()
Sova

3
Я знаю, що коментую старіший пост. Думав згадати, що код KevBurnsJr працює добре. Однак нещодавно я щойно перейшов з 32-розрядного сервера Windows 2003 на сервер Windows 2008 R2 x64, і виявляю, що тиражую унікальні хеші. Зараз мені доводиться шукати альтернативний метод створення кодів підтвердження.
DanielJay

2
Публікацію оновлено для використання bcmath за допомогою деяких коментаторів, тож вона повинна бути надійною зараз. Хтось також знайшов спосіб зробити його оборотним, що є абсолютно наркотиком.
KevBurnsJr

2
web.archive.org/web/20130727034425/http://blog.kevburnsjr.com/ ..., здається, веб-сайт не працює, тому ось копія цього посилання;)
Harinder

4
Вам слід відновити свій сайт або опублікувати php-версію цього на github.com/KevBurnsJr/pseudocrypt - яка чудова маленька бібліотека ! Не хотів використовувати гігантську "систему", таку як YOURLS чи PHURL, просто гарна бібліотека для створення коротких посилань, і все. Подяка
inorganik

21

Найкоротший хеш - це довжина 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

32
Використовуючи перші 8 символів md5, існує ймовірний шанс, що дві URL-адреси матимуть однаковий хеш
Том Хей

2
Так, таке зіткнення може статися, але шансів дуже мало для випадкових рядків, це приблизно від одного до 4 мільярдів, однак, якщо ви хочете мати 100% унікальний хеш, який ви можете використовувати як посилання на включений клас записів бази даних.
Назарій

2
хочу згадати, що це const codesetможе бути в будь-якому довільному порядку, просто щоб
затуманити

4

Для короткого хешу , зручного для URL-адреси , з метою заборони можливого повторення вмісту, ми можемо використовувати, hash()особливо тип хешу CRC , оскільки він створений саме для цього:

Циклічна перевірка надмірності

Циклічна перевірка надмірності (CRC) - це код виявлення помилок, який зазвичай використовується в цифрових мережах та пристроях зберігання даних для виявлення випадкових змін необроблених даних. Блоки даних, що надходять у ці системи, отримують коротке контрольне значення, додане на основі залишку поліноміального поділу їх вмісту. Після отримання обчислення повторюється, і якщо контрольні значення не збігаються, можна вжити коригувальних дій

https://en.wikipedia.org/wiki/Cyclic_redundancy_check

echo hash("crc32", "Content of article...");
// Output fd3e7c6e

2

І все-таки найкраща відповідь: найменший унікальний рядок "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;

}

?>

1

Насправді найкращим рішенням для "випадкового" хешу є створення списку випадкових хешів, розміщення його на Mysql з унікальним ІНДЕКСОМ (ви можете написати простий UDF, щоб вставити 100 000 рядків за 1 секунду).

Я думаю, що така структура, як цей | HASH | СТАТУС | URL | ПЕРЕГЛЯДИ | ......

Де статус вказує, безкоштовний цей Хеш чи ні.


0

Простий спосіб перевірки дублікатів у базі даних:

$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; 
    }
}

0

Я робив скорочувач URL-адрес. У моєму випадку я використовував "ідентифікатор" бази даних, щоб створювати кожен раз унікальну коротку URL-адресу.

Що я зробив, це, по-перше -

Вставте такі дані, як "Оригінальна URL-адреса" та "Дата створення", в db, залишивши "коротку URL-адресу" порожньою в db. Потім дістаньте "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" для створення короткого коду. Оскільки ідентифікатор не може бути однаковим для декількох записів. Це унікально, отже, унікальний код або URL-адреса буде унікальним.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.