webew
Войти » Регистрация
 
PHP

Централизация обработки запросов к сайту с помощью mod_rewrite

26 мая 2009, 12:36
Автор: Михаил Серов [1234ru]

Многие программисты создают на сервере структуру файлов и каталогов, точно соответствующую адресам страниц. Нередко это бывает неудобно: требуется создание разветвленной структуры каталогов, повторение определенного количества программного кода в нескольких местах и др. Модуль веб-сервера Apache mod_rewrite позволяет централизовать обработку запросов и сделать ответ сервера независимым от расположения физических файлов и каталогов — грубо говоря, решать, что показать клиенту, будет не файловая система и программист, а только программист, в единоличном порядке. О том, как это осуществить, и пойдет речь в настоящей статье.

Для чего это нужно.

На многих сайтах встречаются страницы вида /news.php?id=123, /products.php?id=45, /about.php, /contact.php и т.п. Как правило, каждой такой странице соответствует отдельный скрипт с точно совпадающими названием и расположением, что уже неприятно, т.к. накладывает определенные ограничения и, таким образом, увеличивает вероятность ошибки при разработке сайта. Что еще (и гораздо) более неприятно: большинство страниц сайта имеет повторяющиеся фрагменты HTML, которые генерируются одним и тем же кодом в скриптах; соответственно, этот код должен быть повторен столько раз, сколько есть скриптов.

Что может дать описываемый подход? Для начала он позволит избавиться от нескольких файлов, не отказываясь при этом от вида адресов /scriptname.php?get=..., и заменить их одним рабочим скриптом (или не одним; в любом случае, столькими, сколькими захочется, а не сколькими потребует формат адреса).

На самом деле такой подход может дать гораздо больше. При таком подходе относительный адрес страницы перестает зависеть от физического расположения файлов и каталогов и становится некой абстрактной строкой, вид которой ограничивается только стандартом URL. Это значит, что при формировании ответа клиенту можно сделать вид, что есть, например, каталог /news/ и страница /news/123.html (или что-то еще более сложное), хотя ни каталога, ни файла с такими названиями в физической файловой системе нет и в помине.

Замена адресов вида /news.php?id=123 и /articles.php?id=45 на адреса вида /news/123.html и /articles/45.html отнюдь не лишена смысла: такой формат адресов обладает лучшей читаемостью, кроме того, есть информация, что для поисковых систем также легче проиндексировать две фактически разные страницы, если они имеют разные адреса, чем в случае, когда адреса отличаются только строкой GET-запроса.

Что такое mod_rewrite.

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

.htaccess

Одним из самых гибких методов настройки сервера Apache является использование конфигурационных файлов .htaccess. Установки из этих файлов имеют более высокий приоритет, чем установки в конфигурационных файлах httpd.conf сервера Apache (если не запрещена замена установок), и действуют на каталог, в котором находится данный файл .htaccess, а также на все дочерние каталоги (если только в каких-либо из них нет своих файлов .htaccess — тогда установки из .htaccess текущего каталога аннулируют установки из .htaccess родительского).

Сейчас наша основная задача состоит в том, чтобы централизовать управление сервером — передать его какому-то одному скрипту, пусть это будет, например, скрипт index.php в корневом каталоге веб-сервера. Поскольку мы хотим перенаправлять таким образом абсолютно любой запрос, файл .htaccess следует создать также в корневом каталоге. Для решения нашей задачи этот файл должен содержать всего две строчки:

RewriteEngine on
RewriteRule .*? index.php

Директива RewriteEngine on отвечает за включение механизма mod_rewrite (без неё он просто не начнет работать). Директива RewiteRule не так проста в использовании и также является исключительно важной в описываемой реализации, поэтому обсудим её подробнее.

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

RewriteRule условие что_делать
(подробнее см. http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#rewriterule).

В качестве условия выступает Perl-совместимое регулярное выражение (mod_rewrite использует библиотеку PCRE), которое сравнивается со строкой URL (если запрошен адрес http://site.ru/news/123.html, то строкой URL будет /news/123.html). Поскольку нам требуется запустить механизм в любом случае, следует составить такое регулярное выражение, которое совпало бы с любой строкой. Для этого хорошо подходит .*?.

Второй аргумент директивы (что_делать) — это указание серверу о том, как ему изменить адрес запроса. Нам нужно абсолютно все запросы направить к скрипту index.php, находящемся в том же каталоге, что и .htaccess; это и отражено в директиве.

Что теперь должен делать index.php

Вот как может выглядеть вышеупомянутый скрипт index.php:

<?php
/*
Здесь и должен формироваться код для всех возможных URL (т.е. для всех страниц).
Если раньше всё это было рассредоточено по разным файлам, то теперь всё - здесь.
По непосредственно рабочему коду отличия минимальные: как формировали HTML специфические
предназначенные для этого функции, так и будут формировать.
Главное - не забыть, что одинаковые части страницы
типа шапки формируются один раз, и не наделать повторяющегося кода.

Следует отметить, что несмотря на манипуляции с адресом запроса,
cодержимое переменной $_SERVER['REQUEST_URI'] останется прежним,
и можно будет работать с тем адресом запроса, который был в самом начале.
*/

$url = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); // GET-запрос пока уберём

// разберем различные случаи вида url.
switch ($url)
    {
    case preg_match('/^\/news\/(\d+)\.html$/', $url, $matches) :
        // обрабатываем страницы вида /news/123.html
        $newsid = $matches[1];
        // далее получаем текст новости по id, внедряем текст в код страницы и т.п.
    break;
   
    case preg_match('/^\/articles\/(.+)\.html$/', $url, $matches) :
        // обрабатываем страницы вида /articles/some_article.html
        $article_name = $matches[1];
        // получаем данные статьи по её имени
    break;
   
    case preg_match('/^\/$/', $url, $matches) :
        // здесь выводим HTML для главной страницы; её адрес - /
        // хотя более удобным было бы для всякой страницы HTML-код
        // всё же формировать динамически
    break;
   
    /* и аналогично - другие случаи вида URL, которые стоит обработать отдельно */
   
    default:
        /*
        Если ничего из вышеперечисленного не сработало, значит, запрошенный адрес
        не относится ни к статье, ни к новости, а относится к чему-либо еще
        (например, к одной из уникальных страниц, вид которых
        строится индивидуально для каждой из них).
        Возможно, страницы с запрашиваемым адресом на сайте не существует,
        тогда клиенту нужно вернуть HTTP-код ошибки - 404 - и информацию о том,
        что страница не найдена.
        */

    break;
    }
   
    /*
    Теперь можно каким-либо способом (например, при помощи какого-нибудь шаблонизатора)
    построить однотипные для всех страниц сайта фрагменты HTML-кода
    (например, объявление DOCTYPE, заголовок и шапку страницы),
    после чего вывести полученный код в окно браузера.
   
    Разумеется, всё это может принимать сколь угодно разнообразные формы.
    */

   
?>

Если все-таки кое-где нужны физические файлы и каталоги.

Бывает, однако, так, что в ряде случаев по тем или иными причинам использовать физические файлы и каталоги удобнее, чем формировать динамический ответ — например, в случае картинок или других двоичных файлов (хотя существует альтернативный способ хранения и обработки файлов, основанный на использовании СУБД). В рамках данного подхода это может быть реализовано двумя способами.

Собственные файлы .htaccess в подкаталогах

Как уже упоминалось, установки в .htaccess текущего каталога имеют приоритет над установками родительских всех уровней. Причем эти установки действуют не только на сам текущий каталог, но и на его дочерние каталоги. Этим можно воспользоваться в каждом случае, когда имеется каталог (пусть /pics/), содержимое которого должно быть доступно напрямую. Для этого нужно в нём создать файл .htaccess, содержащий всего одну строчку:

RewriteEngine off

Такой файл просто снимет все имеющиеся предписания насчет перенаправления запросов для каталога /pics/ и его дочерних каталогов, и изнутри /pics/ всё будет выглядеть так, как будто mod_rewrite вообще не включен.

Следует отметить, что понятие «изнутри каталога» относится также и ко вложенным каталогам — доступ к ним тоже будет прямым, для этого создавать .htaccess в каждом из них не нужно.

Условия !-d и !-f в корневом .htaccess

Этот вариант реализуется на уровне корневого каталога и состоит в ограничении правила RewriteRule определенными условиями.

В общем случае каждому правилу (директиве RewriteRule) может предшествовать одно или несколько условий выполнения этого правила (директив RewriteCond). Правило применяется лишь в том случае, если все предшествующие ему условия выполнены.

В примерах выше мы не использовали условий, поскольку правила должны были действовать в любом случае. Теперь же необходимо составить условия — такие, которые запретили бы правилу действовать в случаях, когда запрашиваемый адрес является путем к физическому файлу, или, немного перефразируя, условия дейсвтия должны быть таковы, чтобы запрошенный адрес не являлся путем к файлу. С учетом всего этого .htaccess будет выглядеть так:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .*? index.php
# в переменной сервера REQUEST_FILENAME как раз и хранится адрес запроса

Такая конфигурация обеспечит доступ не только к файлам в текущем каталоге, но и в дочерних (т.е. для того, чтобы стали видны файлы /pics/123.jpg, специально делать больше ничего не понадобится). В то же время, адреса, соответствующие физическим каталогам, по-прежнему будут переписываться. Это приведет к тому, что списки содержимого каталогов просматривать будет нельзя (запросив /pics/, вместо списка картинок клиент получит перенаправление на /index.php). Если все-таки нужна видимость и каталогов тоже, то .htaccess следует дополнить соответствующим условием для каталогов, и он примет такой вид:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .*? index.php

Подробнее о составлении условий можно узнать из соответствующей страницы документации к серверу Apache.

Сравнение двух способов.

Первый способ является более безопасным, поскольку общие установки более строгие. Чтобы их отменить, нужно совершить специальные операции — это и повышает безопасность, поскольку программист яснее осознает, какие области он делает доступными. Это же и снижает удобство: если безопасность не нужна, то легче написать две строчки в корневом .htaccess, чем проводить отдельную работу для каждой директории.

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


© Все права на данную статью принадлежат порталу webew.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
NO USERPIC

M-A-X

>Замена адресов вида /news.php?id=123 и /articles.php?id=45 на адреса вида /news/123.html и /articles/45.html отнюдь не лишена смысла: такой формат адресов обладает лучшей читаемостью

Та читаемость одинакома. А если вручную менять номера для перехода, то удобнее, чтобы было вида /news.php?id=123, курсор проще в конец строки установить :)

>кроме того, есть информация, что для поисковых систем также легче проиндексировать две фактически разные страницы, если они имеют разные адреса, чем в случае, когда адреса отличаются только строкой GET-запроса.

Уже вроде нет :)

> Собственные файлы .htaccess в подкаталогах

Как уже упоминалось, установки в .htaccess текущего каталога имеют приоритет над установками родительских всех уровней. Причем эти установки действуют не только на сам текущий каталог, но и на его дочерние каталоги. Этим можно воспользоваться в каждом случае, когда имеется каталог (пусть /pics/), содержимое которого должно быть доступно напрямую. Для этого нужно в нём создать файл .htaccess, содержащий всего одну строчку:
RewriteEngine on


Может лучше RewriteEngine off? :)

И есть информация, что mod_rewrite немного нагружает сервер.
http://kpitv.net/
11.08.2009, 01:34
Ответить

1234ru

Цитата:
Та читаемость одинакома. А если вручную менять номера для перехода, то удобнее, чтобы было вида /news.php?id=123, курсор проще в конец строки установить :)

А если /news.php?id=123&view=flat&start=150 ? :)

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

Цитата:
Может лучше RewriteEngine off? :)

И правда :) Поправил статью

Цитата:
>кроме того, есть информация, что для поисковых систем также легче проиндексировать две фактически разные страницы, если они имеют разные адреса, чем в случае, когда адреса отличаются только строкой GET-запроса.

Уже вроде нет :)

И давно это? (ссылку какую-нть бы, что ли...)

Цитата:
И есть информация, что mod_rewrite немного нагружает сервер.

Бывает ли это критично?

Кстати, а как Вы проектируете свои приложения, какими технологиями пользуетесь?
То, что не убивает нас, делает нас инвалидами.
14.08.2009, 22:56
Ответить
NO USERPIC

M-A-X

1234ru
А если /news.php?id=123&view=flat&start=150 ? :)

Ну это уже на форум похоже :)

1234ru
И давно это? (ссылку какую-нть бы, что ли...)


http://promosite.ru/articles/report-optimization-2002.php
Абзац "Геометрия сайта и ее оптимизация"
Ссылка за 2002 год. Думаю, уже точно нет некоторых оставшихся на то время проблем.
Правда, в Яндексе часто при запросе первыми выводятся сайты с входжением ключевого слова в юрл.
Других ссылок под рукой нет. Можно Ваши ссылки? :)

1234ru
Бывает ли это критично?

Встречал такую информацию.

Проверил сам только что.
ab -n 150 -c 10 -g и
ab -n 1500 -c 100 -g
С mod_rewrite и одним условием время отклика не увеличилось.

1234ru
Кстати, а как Вы проектируете свои приложения, какими технологиями пользуетесь?]


Сначала это было хобби.
Как проектирую? Это сложно рассказать. :) Стараюсь, чтобы приложение создавало минимальную нагрузку и было расширяемым. Супер-профи себя не считаю :)
http://kpitv.net/
19.08.2009, 18:50
Ответить

1234ru

Цитата:
Других ссылок под рукой нет. Можно Ваши ссылки? :)

Признаюсь, что ссылками особо не располагаю :о (я сам в этом очень слабо разбираюсь - рассказывали друзья-SEOшники)

Цитата:
Как проектирую? Это сложно рассказать. :) Стараюсь, чтобы приложение создавало минимальную нагрузку и было расширяемым.

Ну, это прекрасно :)

То, что не убивает нас, делает нас инвалидами.
19.08.2009, 23:55
Ответить
NO USERPIC

M-A-X

Все таки да, удобней с одной точкой входа :)
http://kpitv.net/
08.09.2011, 13:59
Ответить
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
© 2007—2012 webew.ru, связаться: x собака webew.ru
Сайт использует Flede и соответствует стандартам WAI-WCAG 1.0 на уровне A.
Rambler's Top100