PHP short hash like URL-shortening websites

2019-01-03 20:24发布

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

The hash should not be longer than 8 characters.

标签: php hash
8条回答
小情绪 Triste *
2楼-- · 2019-01-03 20:45

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; 
    }
}
查看更多
再贱就再见
3楼-- · 2019-01-03 20:45

I was making a url shortner. In my case I used the "id" of database to create every time a unique short url.

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.

<?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.

查看更多
爷的心禁止访问
4楼-- · 2019-01-03 20:48

URL shortening services rather use a auto incremented integer value (like a supplementary database ID) and encode that with Base64 or other encodings to have more information per character (64 instead of just 10 like digits).

查看更多
家丑人穷心不美
5楼-- · 2019-01-03 20:51

Shortest hash is 32 character length, how ever you can use first 8 characters of md5 hash

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

Update: here is another class found here written by Travell Perkins which 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 ;)

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
查看更多
家丑人穷心不美
6楼-- · 2019-01-03 20:51

Best Answer Yet: Smallest Unique "Hash Like" String Given Unique Database ID - PHP Solution, No Third Party Libraries Required.

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;

}

?>
查看更多
beautiful°
7楼-- · 2019-01-03 20:52

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.

Base 36 to Integer:

intval($str, 36);

Integer to Base 36:

base_convert($val, 10, 36);

So then, instead of redirecting to a route like /url/1234 it becomes /url/ax instead. 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.

Don't hash, use other bases for this kind of thing. (It's faster and can be made collision-proof.)

查看更多
登录 后发表回答