1 августа, 2011

htaccess

ЧПУ на mod_rewrite

Вступление

В этой статья я расскажу как я «разобрался» с ЧПУ. Дело до них доходило и ранее, но я использовал сторонние коды, это иногда приводило к результатам которых я не ждал или не понимал.

Я начал прогугливать это тему по одной причине: в сервисе яндекса (яндекс-вебмастер) можно было наблюдать только одну страницу моего сайта /index.php?. Нет, проиндексирована была не одна страница, но чувствуется, что отношение поисковика к этим динамическим страницам как к убогим. Я находил статьи в которых говорилось, что нет разницы для поисковиков ЧПУ ведет к страницам, или нет, но мое мнение не изменилось. ИМХО /index.php?do=1&page=2 и /index.php?page=2&do=1#header для поисковика, возможно, разные страницы, хотя наполнение одно и тоже, а если путаться и в разных местах давать разную ссылку – уменьшенный вес обоих страниц. В общем суть статьи не в определении минусов того или другого метода, мне нужна была четкая система составления адресов страниц.

Начал с обзора аналогов (прям как в универе при написании курсовой) и обнаружил некоторые неприятный вещи. На одном сайте можно было с урл проделывать следующие фокусы, допустим урл вида: /category/section/index.html я могу добавить в путь любую белеберду /category/section/gavno_chpu/index.html, причем статья откроется нормально, 404 не выдаст. Я постараюсь сделать более жесткую систему!

О методе создания ЧПУ (с помощью mod_rewrite) и пару полезных ссылок.

Статью уважаемого spectator.ru прокопипэстили не одну сотню раз. И писать о том какие методы есть для создания ЧПУ я не буду.

Для этих целей есть специальный модуль в Апаче, который называется mod_rewrite. Он позволяет «переписывывать урлы», то есть, преобразовывать их «на лету» по правилам, которые вы ему опишите.

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

Довольно полную документацию можно посмотреть тут. Но она довольно сухая и тем кто сталкивается впервые с модулем mod_rewrite, будет непросто в ней разобраться.

Еще пару полезных ссылок и этого будет достаточно:

Не работает модуль mod_rewrite? Как проверить подключен ли модуль?

проверить, установлен ли модуль на веб сервере

  1. Проверить файл конфигурации Apache (httpd.conf)
  2. Проверить работу сервера с примерами. Если сервер работает без ошибок – mod_rewrite установлен. Если нет, получите сообщение при запросе любой web-страницы: «Внутренняя ошибка сервера»

RewriteEngine on – включает модуль, без него последующие директивы не будут учитываться.

Как работает mod_rewrite

Про то как работает mod_rewrite очень подроно написано в вышеприведенной документации. Я приведу некоторые основные моменты для того, чтобы документация не казалась набором слов крутого программиста. Мои объяснения, возможно, будут грубыми или даже совсем неприемлимыми, но RTFM будет читаться на полном ходу.

Директивы — команды, составная часть языка. Для создания ЧПУ чаще всего используют две дериктивы: RewriteCond и RewriteRule.

RewriteCond — условие при котором будет срабатывать RewriteRule.

RewriteRule — собственно преобразует URL, так же содержит в себе условие (шаблон) при котором будет URL «схвачен» и преобразован.

Синтаксис следующий:

RewriteCond Сравниваемая Строка Условие
RewriteRule Шаблон Подстановка

Такми образом уже есть два условия, одно в Rule (называемое Шаблоном) другое в Cond, попробуем разобраться на кошках. В адресной строке вобъем несуществующую страницу your-site.ru/novaya_statya.html, а в .htaccess будут следующие строки.

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteRule ^([^/]*).html$ /index.php?page=$1 [L,QSA]

В следующей строке RewriteCond берет значение переменной %{REQUEST_FILENAME} (переменная содержащая полный путь в файловой системе сервера к файлу или скрипту, в нашем случае это просто) и проверяет условием !-f («!» — не, «-f» — является обычным файлом). Условие сработало (нет такого файла «novaya_statya.html»), идем дальше.

Условие в RewriteRule (называемое «Шаблон» — привыкайте) записано в виде регульрного выражения «^([^/]*).html$». Наша строка проходит проверку регулярным выражением (регулярное выражение нашло совпадение и сохранило его в переменной $1). Все условии выполнены! RewriteRule перенаправит наш запрос на /index.php?page= novaya_statya

Мы шли по коду сверху вниз и все работало, но на самом деле все происходит совсем не так! Первые пару абзацев в RTFM посвящены именно тому, каким путем идет наш модуль, даже присутствуют соответствующие схемы. Нужно запомнить одну вещь: mod_rewrite сначало заходит в первый RewriteRule, смотрит на совпадение с Шаблоном, в Шаблоне регулярное выражение запоминает в переменные то, что вы захотите, а потом только идет в RewriteCond (соответствующий нашему правилу преобразования) и делает проверку условия. Это так же дает возможность в RewriteCond вести проверку того, что вы запомнили в регулярном выражении, а называется это обратной связью.

Теперь бегом читать RTFM!

Жесткая структура для ЧПУ

Многие делают просто:

RewriteEngine On
Options +FollowSymlinks
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) index.php?%{QUERY_STRING}

А потом в PHP разбирают QUERY_STRING по полкам. Но в этот QUERY_STRING может попасть все что угодно и будет необходимо рассмотреть все возможные случаи. Да, «все возможные случаи» — это намек на регулярные выражения. Я заранее продумал, какие ЧПУ будут встречаться на моем сайте. Так почему бы не воспользоваться регулярными выражениями в mod_rewrite и не ограничить логику движка сайта только выводом того, что точно есть?

Вы ведь слышали про «вложенность» страницы. Плохо если пользователю приходится пять раз кликать, чтобы добраться до «целевой страницы»? Может стоить ограничить тогда и ЧПУ, чтобы не допустить /categor/subcategory/subsubcategory/subsubsubcategory/article.html. Да это и не ново, что я вам рассказываю…

Хочу так: максимальное число составляющих урл: sid_категории / sid_раздела / sid_элемента.html

И конечно варианты:
sid_элемента.html
sid_категории / sid_элемента.html
sid_категории / sid_раздела /

Составление регулярных выражении для mod_rewrite, отлов ошибок

При доступе к категории сайта (папке) можно забыть поставить в конце «/», что я успешно практикую, поэтому полный список вариаций возможных URL следующий:

  • sid_элемента.html
  • sid_категории
  • sid_категории /
  • sid_категории / sid_элемента.html
  • sid_категории / sid_раздела
  • sid_категории / sid_раздела /
  • sid_категории / sid_раздела / sid_элемента.html

Я разбил весь список на две части, с «.html» и без них и написал два регулярных выражения (возможно и не стоило так делать, но мне было проще именно так):

^(?:([^/\.]+)/)?(?:([^/\.]+)/)?(?:([^/\.]+)\.html)+$
^(?:([^/\.]+)[/]?)?(?:([^/\.]+)[/]?)?$

Все URL которые соответствуют регулярным выражениям – будут преобразовываться. А с остальными:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ - [R=404]

Извините, ошибка 404, страница не найдена.

Итак, для моего случая:

RewriteEngine on
Options +FollowSymlinks
RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(?:([^/\.]+)[/]?)?(?:([^/\.]+)[/]?)?$ /index.php?sid_category=$1&sid_section=$2 [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(?:([^/\.]+)/)?(?:([^/\.]+)/)?(?:([^/\.]+)\.html)+$ /index.php?sid_category=$1&sid_section=$2&sid_element=$3 [L,QSA]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ - [R=404]

Все остальное, вы уже допишите сами. Спасибо за внимание.


3 комментария к публикации „ЧПУ на mod_rewrite“

1. 6 Февраля, 2012 | Виталя

Почему данный код не хочет работать на Apache 1.3, выдает ошибку: .htaccess: RewriteRule: cannot compile regular expression '^(?:([^/\.] )[/]?)?(?:([^/\.] )[/]?)?$' т.е. неверное регулярное выражение. Как его модифицировать что бы работало на apach 1.3?

2. 10 Февраля, 2012 | Владимир

Спасибо Вам огромное за толковое объяснение! Без мелкой детальки не мог добиться расширения .php в конце URL.

3. 11 Февраля, 2012 | Максим Златов

to Виталя: Пропустили плюсик после ^(?:([^/.] хотя может у меня в сообщениях плюсики убираются проверка: ^(?:([^/.] )[/]?)?(?:([^/.] )[/]?)?$

Добавить комментарий

2010–2018 Блог Максима Златова, контакты

PHP execution time: 0.0114 s.
SQL execution time: 0.0023 s. (select publication with comments)

Яндекс.Метрика