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

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

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

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

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 и именах файлов.

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

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.