URL 단축 웹 사이트와 같은 PHP 짧은 해시


83

다음과 같은 URL 단축 웹 사이트와 유사한 문자열 또는 파일에서 짧은 해시를 생성하는 PHP 함수를 찾고 있습니다. tinyurl.com 있습니다.

해시는 8 자 이하 여야합니다.


2
나는 이것이 오래된 질문이라는 것을 알고 있지만 hashids.org를 확인하십시오 . 대부분의 프로그래밍 언어와 함께 작동
그렉

ShortCode 라이브러리를 확인하십시오 . 그것은 당신이 원하는 것을 정확하게 수행합니다. 기본 변환을 기반으로합니다.
Anis

Adler-32 또는 CRC32를 사용하는 것 외에는 현대적인 (충돌 방지) 해시 그다지 줄일 수 없습니다 (예 : 8 자까지). SHA-2도, SHA-1도, MD5도 마찬가지입니다. 를 사용하면 Alphabet::convert($hash, Alphabet::HEX, Alphabet::ALPHANUMERIC)MD5를 32 자에서 22 자까지 줄일 수 있습니다. 대신 원하는 것은 파일의 정수 ID (예 : 데이터베이스)를 (new Id())->encode($id).
2019

답변:


47

URL 단축 서비스는 대신 자동 증분 정수 값 (예 : 보조 데이터베이스 ID)을 사용하고 Base64 또는 기타 인코딩으로 인코딩하여 문자 당 더 많은 정보를 포함합니다 (예 : 10 자리 대신 64 자리).


1
이것이 의미하는 바 (캐릭터 당 더 많은 정보) 그냥 궁금합니다 !!
ravisoni

2
@ravisoni 십진수를 사용하는 경우 0– 숫자 9를 나타 내기 위해 인코딩 된 문자 당 10 개의 가능한 값이 있습니다 (ld (10) ≈ 3.32 비트 / 문자). 그러나 Base64 문자로 동일한 숫자를 나타내는 경우 인코딩 된 문자 당 64 개의 가능한 값이 있습니다 (ld (64) = 6 비트 / 문자). 따라서 Base64를 사용하면 인코딩 된 각 문자에 더 많은 정보, 즉 3.32 비트 대신 6 비트 정보가 저장됩니다.
Gumbo 2015 년

3
base64를 사용하는 경우 스크립트가 for ($ i = 0; $ i <999999; $ i ++) {$ pageContent = fread (fopen ( ' yoururl.com/'.base64_encode($i) );} 이제 데이터베이스의 모든 URL에 액세스 할 수 있습니다.

161

TinyURL은 아무것도 해시하지 않으며 Base 36 정수 (또는 소문자와 대문자를 사용하는 base 62)를 사용하여 방문 할 레코드를 나타냅니다.

기본 36에서 정수로 :

intval($str, 36);

정수를 Base 36으로 :

base_convert($val, 10, 36);

따라서 다음과 같은 경로로 리디렉션하는 대신 /url/1234 것이됩니다 /url/ax대신. 이것은 충돌이 없기 때문에 해시보다 훨씬 더 많이 사용할 수 있습니다. 이를 통해 URL이 존재하는지 쉽게 확인하고 사용자가 이미 데이터베이스에 있음을 알지 못해도 base 36의 적절한 기존 ID를 반환 할 수 있습니다.

해시하지 말고 이런 종류의 것을 위해 다른 기반을 사용하십시오. (더 빠르고 충돌 방지가 가능합니다.)


안녕하세요 @RobertK, PHP는 숫자와 문자를 모두 포함하는 6 자리 문자열을 어떻게 변환할까요?
팀 피터슨

@timpeterson, 단순히 intval을 호출하고 주어진베이스를 전달합니다 (내 첫 번째 코드 블록 참조).
Robert K

@RobertK이지만 intval()모든 것을 숫자로 바꿉니다 . intval()데이터베이스 역할과 같이 리디렉션을 수행하는 데 필요한 다른 단계와 연결 하는 방법에 대해 혼란 스러울 수 있습니다.
팀 피터슨

@timpeterson, 이는 문자열이 데이터베이스 레코드의 ID를 나타 내기 때문입니다. 따라서 전달 된 ID를 기반으로 레코드를 선택합니다.
Robert K

@RobertK는 내가 함께보고있어 하나의 문제는 intval()당신이 경우입니다 $str(-) 거기에있다 슬래시 (/) 또는 대시. 나는 그것을 실현 on/stuff, on-stuff그리고 on모든 수를 반환 887. 슬래시와 대시가있는 URL로 작업 할 수있는 솔루션이 있습니까?
tim peterson

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

2015 년 7 월 14 일 : 찾기 어렵 기 때문에 아래 실제 코드 추가 :

<?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 코드가 잘 작동한다고 언급 할 것이라고 생각했습니다. 그러나 최근에 Windows 2003 32 비트 서버에서 Windows 2008 R2 x64 서버로 전환했고 고유 한 해시를 복제하고 있다는 것을 알게되었습니다. 이제 확인 코드를 만드는 다른 방법을 찾아야합니다.
DanielJay

2
일부 댓글 작성자의 도움으로 bcmath를 사용하도록 게시물이 업데이트되었으므로 이제 견고해야합니다. 누군가는 완전히 마약 인 가역적으로 만드는 방법을 찾았습니다.
KevBurnsJr

2
web.archive.org/web/20130727034425/http://blog.kevburnsjr.com/… 웹 사이트가 다운 된 것 같으므로 여기에 해당 링크의 사본이 있습니다.)
Harinder

4
사이트를 백업하거나 github.com/KevBurnsJr/pseudocrypt 아래에 PHP 버전을 게시 해야합니다. 정말 멋진 lib입니다! YOURLS 또는 PHURL과 같은 거대한 "시스템"을 사용하고 싶지 않았고, 짧은 링크를 만드는 데 좋은 라이브러리입니다. 감사합니다
inorganik

22

가장 짧은 해시는 32 자 길이이며 md5 해시의 처음 8자를 사용할 수 있습니다.

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

업데이트 : 여기에 다른 클래스가 발견 여기에Travell 퍼킨스 레코드 번호를 받아 그것을 짧은 해시를 생성합니다. 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
동일한 해시를 가진 두 개의 URL의 합리적인 기회 아마이 MD5의 처음 8 charactors를 사용
톰 헤이그는

2
예, 그러한 충돌이 발생할 수 있지만 임의의 문자열에 대한 가능성은 거의 없습니다. 약 1 ~ 40 억입니다.하지만 데이터베이스 레코드 사용 포함 클래스에 대한 참조로 사용할 수있는 100 % 고유 해시를 갖고 싶다면.
Nazariy

2
그것은 언급 할 const codeset++ 단지 당황하게하는, 임의의 순서로 될 수있다
루이스 Siquot에게

4

짧은 hash , url friendly의 경우 가능한 중복 콘텐츠를 허용하지 않는다는 관점에서 우리는 hash()특히 CRC 해시 유형을 사용할 수 있습니다 .

순환 중복 검사

CRC (Cyclic Redundancy Check)는 원시 데이터의 우발적 인 변경을 감지하기 위해 디지털 네트워크 및 저장 장치에서 일반적으로 사용되는 오류 감지 코드입니다. 이러한 시스템에 입력되는 데이터 블록은 내용의 나머지 다항식 분할을 기반으로 짧은 검사 값이 첨부됩니다. 검색시 계산이 반복되며 확인 값이 일치하지 않을 경우 수정 조치를 취할 수 있습니다.

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

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

2

최고의 답변 : 고유 한 데이터베이스 ID가 지정된 가장 작은 고유 "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

실제로 "무작위"해시를 사용하는 가장 좋은 방법은 임의의 해시 목록을 생성하여 고유 한 INDEX로 Mysql에 넣는 것입니다 (1 초에 100,000 개의 행을 삽입하는 간단한 UDF를 작성할 수 있음).

이 ID | HASH | STATUS | URL | VIEWS | ......와 같은 구조라고 생각합니다.

상태는이 해시가 사용 가능한지 여부를 나타냅니다.


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을 더 짧게 만들고있었습니다. 제 경우에는 데이터베이스의 "id"를 사용하여 매번 고유 한 짧은 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>";

?>

그런 다음 Database의 Short Url Code 값을 업데이트합니다.

여기서는 "id"를 사용하여 짧은 코드를 만듭니다. 여러 항목에 대해 ID가 같을 수 없기 때문입니다. 고유하므로 고유 코드 또는 URL이 고유합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.