Блог Веб-разработчика.

В данной статье речь пойдет о том, как Вы можете организовать перенаправление, (или редирект, другими словами) программными средствами php. Следует отметить, что реализовывать перенаправление силами php, чаще всего имеет смысл, если требуется какая-то обработка пришедших данных, их модификация или проверка, по результатам которой происходит перенаправление. Именно такие реализации php редиректа я буду описывать в этой статье.

Если же, Ваша задача состоит в том, чтобы осуществить перенаправление без каких-либо условий, проверок и модификаций запросов (например, у Вашего сайта сменился домен, и теперь необходимо все старые ссылки с прежнего доменного имени перенаправить на новый домен с указанием кода 301. Или на Вашем сайте единичные материалы сменили адрес.) то Вам скорее всего проще настроить перенаправление через настройки сервера, путем задания директив перенаправления в файле .htaccess Вашего сервера (Apache). Подробнее о редиректе средствами .htaccess Вы можете прочитать "здесь".

Основа php редиректа.

В основе редиректа на php лежит использование функции (справка):

header ( string $string [, bool $replace = true [, int $http_response_code ]] )

Где:

  • $string – строка заголовка;
  • $replace – необязательный параметр, означающий нужно ли перезаписывать ранее указанный аналогичный заголовок, отправленный в браузер пользователя (по умолчанию «true»);
  • $http_response_code – необязательный параметр принудительного задания кода HTTP ответа, если таковой не был задан в ранее переданном заголовке, и если строка заголовка не пуста. По умолчанию передается код «302 Moved Temporarily» - «временно перемещено».

Для того, чтобы осуществить непосредственный редирект, необходимо чтобы был указан специальный вид строки заголовка «location: …». Например, так:

header('Location: http://www.example.com/');

в данном случае, после отправки заголовка, пользователь будет перенаправлен на любой сайт, указанный вместо «http://www.example.com/».

Важно отметить, что согласно спецификациии HTTP/1.1 необходимо в качестве аргумента «location» указывать абсолютный путь, с указанием протокола подключения (например: http), имени хоста (домен сайта) и пути назначения. Это требование прямо указано в справке по функции header().

Передача кода HTTP статуса при редиректе (301/302).

Как уже было указано Выше, по умолчанию при передаче строки заголовка типа «location», передается статус HTTP ответа «302 Moved Temporarily» - «материал временно перемещен». Что может быть для нас не всегда удобно: дело в том что, если Вы таким образом перенаправляете пользователей со старого адреса материала на новый, то поисковые системы не будут менять прежний адрес материала в индексе, он ведь «временно перемещен»… Чтобы изменить адрес материала в поисковом индексе, необходимо указывать код «301 Moved Permanently» - «перемещено постоянно» или «302 Found» - «найдено». А иногда нужно отдать заголовок «200 OK» - «хорошо», означающий что запрошенный материал успешно передан в браузер пользователя.

Коды HTTP ответа можно задавать принудительно двумя различными способами. Первый способ следует из спецификации самой функции header, в которой в качестве третьего параметра можно указывать код статуса HTTP, например:

header( 'Location: http://www.example.com/', true, 301 );

Второй способ принудительной передачи кода HTTP ответа – отправка строки заголовка иного вида, перед отправкой заголовка «location». В большинстве случаев это:

header('HTTP/1.1 301 Moved Permanently');
header('Location: http://www.example.com/');

Однако, если на Вашем сервере включен режим FastCGI, то строка заголовка со статусом ответа должна быть несколько иной:

header('Status: 301 Moved Permanently');
header('Location: http://www.example.com/');

Php-редирект с условием.

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

Например, после авторизации пользователя идет обращение к базе данных с целью определения типа пользователя («администратор», «автор» или «читатель»), и в зависимости от того какой тип пользователя, происходит перенаправление в соответствующие разделы сайта. Саму авторизацию и извлечение данных рассматривать не будем, рассмотрим лишь способы организации проверки и отправки соотвествующего заголовка «location».

Мне представляется три наиболее логичных способа реализации такой возможности. Первый, заключается в последовательной проверке типа пользователя условиями «if … elseif …. else». Это может выглядеть примерно следующим образом:

// ранее был определен тип текущего пользователя и помещен в переменную $user_type, далее мы эту переменную проверяем.
if ($user_type == "subscriber") {$redirect_url = "/blog.html";}
elseif ($user_type == "author") {$redirect_url = "/author-panel.html";}
elseif ($user_type == "admin") {$redirect_url = "/admin-panel.html";}
else {$redirect_url = "/registration-form.html";}
 
header('HTTP/1.1 200 OK');
header('Location: http://'.$_SERVER['HTTP_HOST'].$redirect_url);
exit();
 

Второй способ, связан с использованием конструкции «switch … case … ». Это может выглядеть примерно следующим образом:

// ранее был определен тип текущего пользователя и помещен в переменную $user_type, далее мы эту переменную проверяем.
switch ($user_type){
  case "subscriber": $redirect_url = "/blog.html"; break;
  case "author": $redirect_url = "/author-panel.html"; break;
  case "admin": $redirect_url = "/admin-panel.html"; break;
  default: $redirect_url = "/registration-form.html";
}
 
header('HTTP/1.1 200 OK');
header('Location: http://'.$_SERVER['HTTP_HOST'].$redirect_url);
exit();
 

Третий – это использование массива, в котором элементами являются адреса перенаправления, а ключами этих элементов типы пользователей. Это возможно реализовать примерно так:

// ранее был определен тип текущего пользователя и помещен в переменную $user_type, далее мы эту переменную используем для извлечения соответствующего элемента массива.
$redirect_url = array (
  "subscriber" => "/blog.html",
  "author" => "/author-panel.html",
  "admin" => "/admin-panel.html",
  "newuser" => "/registration-form.html"
);
 
header('HTTP/1.1 200 OK');
header('Location: http://'.$_SERVER['HTTP_HOST'].$redirect_url[$user_type]);
exit();
 

Третий вариант, мне кажется наиболее красивым и простым.

Передача сессии при php-редиректе и отключенных куках в браузере клиента.

Если Вы в своих скриптах используете сессии, то при работе с функцией header() важно понимать ее некоторую особенность.

Большинство серверов сконфигурированы таким образом, что работа сесссий поддерживается через так называемые сессионные куки, отправляемые в браузер пользователя при старте сессии. И эти же куки считываются при каждом следующем обращении к страницам сайта, на которых стартуется сессия. Однако возможна такая ситуация, что куки в браузере пользователя отключены. Тогда казалось бы сессии должны перестать работать. Однако…

В интерпретаторе php для сессий такая ситуация предусмотрена. В этом случае при выводе ссылок и форм, php автоматически дописывает ко всем ссылкам и формам, ведущим на внутренние страницы сайта, идентификаторы сессии, которые при переходе по ссылке или при отправке данных с формы передаются на другую страницу сайта методом POST или GET. Таким образом на другой странице сайта, запускается сессия этого же пользователя, хотя куки у него отключены.

Проблема заключается в том, что для заголовков header(“location: …”)  такая функция не предусмотрена. Если в браузере пользователя куки включены – то, как правило, никаких проблем с сессиями при header-редиректе не возникнет. А вот если – отключены, то сессия пользователя не сможет быть открыта заново на другой странице сайта (и пользователю, например, будет заблокирован доступ к контенту). Поэтому, в случае отключенных кук требуется вручную приписать идентификатор сессии к URL-адресу редиректа. Таким образом, идентификатор сессии будет передан методом GET:

header("location: http://www.site.ru/end.php?".session_name().'='.session_id());

Однако, этот метод сработает только в том случае, если параметр сервера «session.use_only_cookies» стоит в значении «0», то есть индентификатор сессии можно передавать как через куки, так и через адресную строку браузера (метод GET) или через онлайн форму (методом POST). Если же параметр стоит в значении «1» - передача идентификатора сессии возможна только через куки. Это настройка безопасности.

Если у Вас этот параметр в значении «1», то чтобы не менять глобальные настройки сервера в php.ini, Вы можете в файле-приемнике (на который будет идти перенаправление), перед стартом сессии вставить код:

ini_set('session.use_only_cookies', 0);

то есть локально, для данного файла, этот параметр перевести в значение «0». После чего выводите старт сессии:

ini_set('session.use_only_cookies', 0);
session_start();
//… далее остальной код вашего скрипта.

Осталось лишь теперь достоверно знать: включены ли куки в браузере пользователя? И в зависимости от этого передавать или не передавать идентификатор сессии в виде GET-параметра запроса. Скрипт, с помощью которого можно определить доступность cookies в браузере пользователей Вашего сайта можно подсмотреть здесь.

Безопасность!!!

Конечно, отключенные куки в последнее время – это нонсенс, такое встретить уже трудно, но если Вы все же захотите адаптировать свои скрипты и под таких пользователей, то обязательно задумайтесь о безопасности. Я имею ввиду- задумайтесь над тем, как формируется URL-аргумент заголовка «Location». Предусмотрите все так, чтобы ни в коем случае не было возможности в качестве аргумента вписать внешний адрес! Иначе возможна ситуация, когда идентификатор сессии пользователя может перейти на удаленный сервер и соответсвенно использован для несанкционированного доступа к сайту от лица пользователя, чья сессия оказалась перехвачена. Это грозит, как минимум, раскрытием личной информации пользователя зарегистрированного в Вашей системе, как максимум (если перехвачена сессия администратора сайта) - потерей контроля над интернет-ресурсом. Так что будьте внимательны!

Редирект при переходе по внешней ссылке с сайта и скрытие реферальной ссылки.

По ряду причин, бывает выгодно все внешние ссылки с сайта сделать не прямыми, а пропустить редиректом через некий служебный файл Вашего сайта, возможно даже с задержкой на секунд 20-30. Это может быть выгодно, если Вы планируете зарабатывать на сайте с помощью различных бирж ссылок, так же отсутствие прямых внешних ссылок якобы улучшает индексацию сайта поисковыми системами. Хотя, это спорный вопрос. В конце концов, редирект внешних ссылок может быть интересен тем, кто не хочет «светить» посетителям своего сайта реферальные ссылки, для многих администраторов блогов это кажется важным.

В целом, принцип редиректа внешних ссылок можно изложить примерно таким образом. Вы создаете служебный файл редиректа (redirector.php), с примерно таким содержимым:

if (isset($_GET['url'])){
     header('HTTP/1.1 200 OK');
     header('Location: '.$_GET['url']);
     exit();
}

а в тексте статей Вашего сайта ссылки прописывать следующим образом:

<a href=”http://vash_site.ru/redirector.php?url=http://vneshniy-site.ru”>ссылка</a>

И таким образом при каждом нажатии на ссылку, пользователь будет сначала переходить на файл redirector.php и тут же перенаправляться на тот URL, который указан в GET параметре «url» запроса.

Но, это лишь принцип. Сама по себе, приведенная реализация довольно топорна. Во-первых, по-хорошему нужно проверять регулярным выражением все, что приходит с GET-запросом. Во-вторых, по-хорошему нужно уберечься от того, чтобы никому не пришло в голову пользоваться Вашим файлом перенаправления, для того чтобы перенаправлять пользователей на свои сайты или на зараженные сайты. Ведь ничто не мешает такую же ссылку на этот файл опубликовать на любом другом блоге и это сработает. Это значит, что нужно как-то обезопаситься. Можно проверять параметр «REFERRER», чтобы убедиться что запрос пришел со своего сайта. Но, я точно знаю что этот параметр можно подделать – поэтому такая проверка не надежна. Да и вообще, писать ссылку и внутри этой ссылки в качестве GET-параметра еще одну ссылку – это как-то не красиво… В-третьих, если на Вашем блоге уже есть много внешних ссылок, то их всех придется переделывать руками – что очень не удобно. Я предпочитаю автоматизировать.

Поэтому мне видится другая, более красивая на мой взгляд реализация. Например, при создании новой статьи на Вашем блоге, вы можете совершенно спокойно публиковать в ней внешние ссылки «как есть». Но при выводе текста статьи на лицевой стороне сайта, воспользоваться php-функцией:

Preg_match_all()

С помощью которой, можно осуществить глобальный поиск ссылок по всей статье. Если ссылка ведет на внешний ресурс, то саму ссылку записать в базу данных, а в тексте статьи заменить ее на синоним, который является ссылкой на redirector.php и в ней передать идентификатор исходной ссылки. В общем, код обрабатывающий текст статьи на выводе (или при сохранении статьи) может быть примерно такой (для ясности, специально вставил комментарии к каждой строке):

//в переменную $text помещен текст статьи
// ищем все не относительные ссылки и результат поиска помещаем в массив
preg_match_all("/<a[\sa-zA-Z0-9=\"\;#:\(\),]*href=\"(http:\/\/[a-zA-Z0-9\.\-\/\?\&amp;_=\s]*)\"[\sa-zA-Z0-9=\"\;#:\(\),]*>/",$text,$externalURLs);
// все найденные неотносительные ссылки проходят через цикл
foreach ($externalURLs[1] as $url){
  // извлекаем из ссылки ее хост
  $url_host = parse_url ($url,PHP_URL_HOST);
  // если хост не совпадает с хостом родного сайта (то есть ссылка внешняя)
  if ($url_host != $_SERVER['HTTP_HOST'] and $url_host != 'www'.$_SERVER['HTTP_HOST']) {
    // то пытаемся из базы данных извлечь информацию об этой ссылке (если она была ренее туда записана)
    $res = $mysqli->query("SELECT * FROM urls WHERE url='".$url."'");
    // если ничего не найдено
    if ($res->num_rows == 0){
      // записываем эту ссылку в базу данных
      $mysqli->query("INSERT INTO urls (`url`) VALUES ('".$url."')");
      //и узнаем ее url_id
      $url_id = $mysqli->insert_id;
      // производим замену этого url в статье на ссылку к скрпипту редиректа с указанием id ссылки в базе данных.
      $text = str_replace($url,"/redirector.php?url_id=".$url_id,$text);
    } else {
      // если же в базе данных запись об этой ссылке уже есть
      $url_info = $res->fetch_array();
      // то просто производим замену ее в тексте на ее синоним.
      $text = str_replace($url,"/redirector.php?url_id=".$url_info['url_id'],$text);
    }
  }
}
echo $text;
 

таким образом при выводе или при сохранении статьи, все внешние ссылки автоматически буду заменены на ссылку к скрипту redirector.php, в которой в качестве GET-параметра указан id ссылки. Скрипт можно усовершенствовать так, чтобы подставлялся не id ссылки, а транслитерация текста ссылки, что возможно будет более эффективно с точки зрения индексации ПС.

Сам скрипт redirector.php может содержать примерно такой код (с комментариями):

// проверяем наличие параметра url_id и проверяем его содержимое
if (isset($_GET['url_id']) and preg_match("/^[0-9]{1,500}$/",$_GET['url_id'])){
  // подключаемся к базе данных (использовать свои данные)
  // $mysqli = new mysqli("хост", "имя пользователя", "пароль", "имя базы данных");
  $mysqli = new mysqli("localhost", "root", "", "tpassed");
  // извлекаем из базы ссылку с присланным номером
  $res = $mysqli->query("SELECT * FROM urls WHERE url_id='".$_GET['url_id']."'");
  if ($res->num_rows != 0) {
    // если такой номер найден
    $url = $res->fetch_array();
    header('HTTP/1.1 200 OK');
    // то по полученной ссылке перенаправляем
    header('Location: '.$url['url']);
    exit();
  } else {
    // если такого номера в базе нет, то завершаем работу и выводим сообщение "ссылка не найдена".
    exit('Ссылка не найдена.');
  }
}
 

При этом структура таблицы «urls» этих ссылок в базе данных, такова:

То есть, в таблице два поля: числовое поле url-id, значение которого генерируется автоматически при создании новой записи; и строковое поле url – собственно значение самой ссылки.

Таким образом, перейдя по ссылке в статье, пользователь гарантированно будет перенаправлен на внешний ресурс, который подразумевался под этой ссылкой. Плюс ко всему, нет необходимости переписывать руками все уже имеющиеся ссылки во всех статьях Вашего блога – они будут заменены «одним махом».

Кстати говоря, думаю не нужно объяснять, что работа приведенного кода не делает исключений для реферральных ссылок. Поэтому, это еще и хороший способ их скрыть, если Вам именно это необходимо.

Редирект с задержкой + Вывод обратного отсчета оставшегося времени.

Иногда на сайтах можно видеть при попытке перейти по внешней ссылке сообщение типа «Вы покидаете наш сайт. Вы будете переадресованы автоматически через N секунд.» и идет обратный отсчет секунд.

Вообще, есть как минимум 4 различных способа вызвать задержку перенаправления. Можно вызвать паузу в исполнении php-скрипта, непосредственно перед отправкой заголовка  «location»:

sleep(15);
header('Location: '.$url['url']);
 

задержка произойдет на 15 секунд. Но недостаток этого метода, в том что пользователь может быть введен в заблуждение, тем что ему придется эти 15 секунд ждать на той же странице, на которой он кликнул – выглядит так, будто бы ссылка не работает, а потом вдруг «бах» и перенаправило… Не очень удобно.

Второй способ: это немного модифицировать отправляемый заголовок, и вместо «location» написать:

header('Refresh: 15; URL='.$url['url']);

задержка снова произойдет на 15 секунд, при этом визуально откроется страница, которую возвращает скрипт redirector.php, что может быть удобно для того чтобы вывести какую-то полезную информацию.

Третий способ, связан с выводом в разеле head html документа тега <meta http-equiv="Refresh" …> в нем же указывается время задержки и адрес перенаправления. Это может выглядеть примерно так:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Перенаправление с задержкой</title>
 
<?php echo '<meta http-equiv="Refresh" content="15; url='.$url['url'].'">';?>
 
</head>
<body>
</body>
</html>
 

Задержка также 15 секунд и при этом открывается страница, с которой непосредственно идет редирект на целевой адрес.

Второй метод связан с отправкой специфического заголовка, третий – выводом html в разделе head документа, а четвертый – с выводом javascript кода, в котором указывается целевой адрес и задержка в милисекундах. Например так:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Перенаправление с задержкой</title>
 
<?php echo '<script type="text/javascript">setTimeout(function(){location.replace("'.$url['url'].'");}, 15000);</script>';?>
 
</head>
<body>
</body>
</html>
 

Остался последний штрих. В последних трех способах реализации задержки, есть возможность встроить javascript таймер обратного отсчета времени, чтобы пользователи имели представление о времени, через которое произойдет перенаправление.

Сам код скрипта таймера кратко разбирается в здесь. Остается только подключить предлагаемый код таймера обратного отсчета к скрипту перенаправления. На примере второго способа редиректа, из предложенных в этой статье, это может выглядеть примерно так:

<?php            header('Refresh: 15; URL=http://www.webengineer.pro/');            ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="robots" content="index, follow" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Редирект с задержкой и обратным отсчетом времени.</title>
</head>
 
<body onLoad="tiktak();">
            <div>
            <script language="JavaScript" type="text/javascript">
                        // значение начальной секунды
                        var second=15;
                        function tiktak()
                        {
                         if(second<=9){second="0" + second;}
                         if(document.getElementById){timer.innerHTML=second;}
                         if(second==00){return false;}
                         second--;
                         setTimeout("tiktak()", 1000);
                        }
            </script>
            Вы будете перенаправлены через <span id="timer"></span> секунд на главную страницу сайта.
            </div>
</body>
</html>
 

Таким образом, задается задержка 15 секунд перед редиректом пользователя, и в течение всего времени идет обратный отсчет времени внутри фразы «Вы будете перенаправлены автоматически через … секунд.». Пример работы здесь. При этом Вы можете как угодно задавать форматирование этой строки через css - таблицы стилей.

Если информация этой статьи будет интересна и полезна Вашему кругу друзей и знакомых, то Вы можете опубликовать ссылку - тогда им проще будет ее найти. Они Вам будут благодарны:).

Комментарии к статье:


Всеволод Чупрыгин © webengineer.pro 2014. Все права защищены.
Копирование материалов сайта разрешено только с указанием имени автора (Всеволод Чупрыгин) и прямой индексируемой ссылки на источник на сайте www.WebEngineer.pro.
ИП Чупрыгин Всеволод Андреевич, ИНН: 333410747832, ОГРН: 311333426300044
http://vkontakte.ru/chuprygin_va, Google +

.
Проверить аттестат
Мы принимаем Webmoney Мы принимаем практически все платежи через Robokassa Мы принимаем Яндекс.Деньги Мы принимаем платежи через QIWI. Мы принимаем платежи через привязанные к QIWI карты VISA/Mastercard.