Избавляемся от внешних ссылок

Опубликовано 25.09.2010 в 12:45 в разделе ,

Создавая свои проекты, веб-проекты, многие программисты и веб-мастера зачастую сталкиваются с проблемой внешних ссылок в теле сайта. Наличие внешних ссылок неблагоприятно сказывается как на рейтинге сайта (бесплатно отдавать свой ТИЦ и PR другим никто не хочет), так и на его стоимости в случае продажи ссылок.

Устранение внешних ссылок — занятие простое. Даже очень. Достаточно внести в свою систему достаточно простой код.

Для начала, уточню, что  поиск внешних ссылок в теле сайта легко делается регулярным выражением высотой менее трёх этажей. Хабровцы могут со мной поспорить, но это именно так.

Чтобы найти все ссылки на сайте, можно применить  следующее регулярное выражение:

/<a (.*?)href=[\"\']([a-z0-9]+)\:\/\/(.*?)\/(.*?)[\"\'](.*?)>(.*?)<\/a>/i

Единственный его минус — это выражение находит все ссылки, как внешние, так и внутренние. Мы можем слегка доработать его в рамках php и добавить распознавание хоста:

$pattern = '/<a (.*?)href=[\"\']([a-z0-9]+)\:\/\/(?!'.$host.')(.*?)\/?(.*?)[\"\'](.*?)>(.*?)<\/a>/i';

В данном случае, $host содержит в себе (о да!) имя хоста, на котором расположен сайт, например, www.av13.ru. Получить его можно любым способом, например, какой-либо мифической системной функцией get_my_host_name или просто через $_SERVER[‘HTTP_HOST’]. Далее, все ссылки нам требуется заменить на внутренние. Для замены мы пользуемся регулярным выражением, описанным выше. В итоге, выходит такая фот функция:

function unurl( $text ) {
	$host = strtr($_SERVER['HTTP_HOST'], array('.' => '\.'));
	$pattern = '/<a (.*?)href=[\"\']([a-z0-9]+)\:\/\/(?!'.$host.')(.*?)\/?(.*?)[\"\'](.*?)>(.*?)<\/a>/i';
	if ( defined('USER_FRIENDLY_URL') ) {
		$text = preg_replace_callback( $pattern, 'text_unurl', $text );
	} else $text = preg_replace_callback( $pattern, 'text_unurl_bad', $text );
	return $text;
}

Как видно из кода, мы используем константу USER_FRIENDLY_URL. Предполагается, что она установлена, если наша система поддерживает ЧПУ, и не установлена, если все ссылки по прежнему работают как ?foo=bar. В зависимости от этого, выбирается одна из функций преобразования:

function text_unurl( $text ) {
	return '<a ' . $text[1] . 'href="/goto/' . $text[2] . '/' . $text[3] . '/' . $text[4] . '"' . $text[5] . '>' . $text[6] . '</a>';
}

function text_unurl_bad( $text ) {
	return '<a ' . $text[1] . 'href="/?goto=' . $text[2] . '://' . $text[3] . '/' . $text[4] . '"' . $text[5] . '>' . $text[6] . '</a>';
}

Первая функция делает ссылки вида http://www.av13.ru/goto/http/vodki.net/pivo/, второй вариант предпочитает ссылки старого образца с прямой передачей параметров, как например http://www.av13.ru/?goto=http://vodki.net/pivo/ — оба варианта будут работать.

После того, как эта функция создана, мы должны доработать ещё два файла в системе. Это наш index.php и .htaccess, на пречи которых будет возложена «сложнейшая» задача трансляции адресов.

Добавим в наш .htaccess:

RewriteEngine On
RewriteRule  ^goto/([a-z0-9]+)/(.*)$  $1://$2  [L,R=301]

Добавим в index.php перед основным кодом:

if ($goto = validurl($_GET['goto'])) {
	header( "Location: $goto" );
	die();
}

Конечно, в случае с index.php желательно добавить ещё и некоторую функцию проверки url-а на валидность — validurl.

function validurl ( $url ) {
	if ( $url ) {
		$urlregex = "^(https?|ftp)\:\/\/([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*(\:[0-9]{2,5})?(\/([a-z0-9+\$_-]\.?)+)*\/?(\?[a-z+&\$_.-][a-z0-9;:@/&%=+\$_.-]*)?(#[a-z_.-][a-z0-9+\$_.-]*)?\$";
		return (eregi($urlregex, $url)) ? $url : '';
	} else return '';
}

Вот и всё — таким после этого остаётся просто выводить весь текст сайта не просто через echo $text, а через echo unurl($text). Но, если лень, можно выполнить такую доработку в системы.

В самом начале (после добавленного редиректа) вставляем вот такую штучку:

ob_start();
ob_implicit_flush(0);

И после завершения всего вывода нашего скрипта, добавим следующее:

$html = ob_get_contents();
ob_end_clean();
echo unurl( $html );

Вот и всё =) Таким образом, скрипт будет сохранять весь вывод, а потом проверять его ururl-ом. Сказать честно, простая проверка намного лучше этого варианта, но увы — лень сподвигнет и не на такое.

Пользователи WordPress могут не заниматься такими изращениями, а просто поставить себе плагин WP No External Links — он сам сделает всё за вас.