Году в 2006 мы разрабатывали свой клон Ютюба. В этом проекте многие вещи пришлось разрабатывать с нуля. Например, мне очень хотелось использовать в проекте хеши подобные тем, что использует Ютюб. С ходу решения не нашлось и пришлось поразмышлять:
- Хеш Ютюба содержит 11 символов.
- Алфавит хеша состоит из 64 символов (по 26 заглавных и строчных букв латинского алфавита, 10 цифр, дефис и символ подчеркивания).
- На кодирование всех символов этого алфавита нужно 6 бит (2^6 = 64).
- Хеш Ютюба содержит 11 символов, т.е. 66 бит.
- Можно предположить что берется какой-то хеш в 64 бита, например половина MD5, дополняется до 66 и дробится на куски по 6 бит.
- Каждый кусок — это символ нашего алфавита.
Тогда у меня получился такой код:
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 и именах файлов.
В итоге получилась такая функция:
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.