Ютюбоподобный хеш

Году в 2006 мы разрабатывали свой клон Ютюба. В этом проекте многие вещи пришлось разрабатывать с нуля. Например, мне очень хотелось использовать в проекте хеши подобные тем, что использует Ютюб. С ходу решения не нашлось и пришлось поразмышлять:

  1. Хеш Ютюба содержит 11 символов.
  2. Алфавит хеша состоит из 64 символов (по 26 заглавных и строчных букв латинского алфавита, 10 цифр, дефис и символ подчеркивания).
  3. На кодирование всех символов этого алфавита нужно 6 бит (2^6 = 64).
  4. Хеш Ютюба содержит 11 символов, т.е. 66 бит.
  5. Можно предположить что берется какой-то хеш в 64 бита, например половина MD5, дополняется до 66 и дробится на куски по 6 бит.
  6. Каждый кусок — это символ нашего алфавита.

Тогда у меня получился такой код:

ylh.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function get_ylhash($data=null, $long=false)
{
    if (is_null($data))
    {
        if ( substr(PHP_VERSION, 0, 1) == '5')
        {   // PHP5
            $data = md5(uniqid(rand(), true), true);
        }
        else
        {   // < PHP5
            $data = md5(uniqid(rand(), true));
            $data = pack('H*', $data);
        }
    }
 
    if (!$long)
    {
        $data = substr($data, 0, 8);
    }
 
    $alphabet = '0123456789_abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $result = '';
    $m = $a = 0;
    for ($i=0, $len=strlen($data); $i<$len; $i++)
    {
        $c = ord($data{$i});
        $result .= $alphabet{($c << $m | $a) & 63};
        $a = $c >> (6-$m);
        $m += 2;
        if ( ($m==6) || ($i==$len-1) )
        {
            $result .= $alphabet{$a};
            $m = $a = 0;
        }
    }
 
    return $result;
}

Шли годы, я использовал эти ютюбоподобные хеши в дальнейших проектах, пока до меня наконец дошло: преобразование бинарной последовательности в строку состоящую из символов алфавита с 64 знаками называется Base64. Немного погуглив я нашел rfc3548.txt, в разделе 4 которого описан стандарт Base64-кодирования для использования в URL и именах файлов.

В итоге получилась такая функция:

ylh.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function get_ylhash($data=null, $long=false)
{
    $trans = array
    (
        '+' => '-',
        '/' => '_',
        '=' => '',
    );
 
    if (is_null($data))
    {
        $data = crypt( mt_rand().uniqid(), mt_rand().uniqid() );
    }
 
    $data = md5($data, true);
 
    if(!$long)
    {
        $data = substr($data, 0, 8);
    }
 
    $data = strtr(base64_encode($data), $trans);
 
    return $data;
}

Исходный код на GitHub.