Полное руководство по mod_rewrite (часть 2): Регулярные выражения


Оглавление. Полное руководство по mod_rewrite

1. Как включить и как работает mod_rewrite

2. Регулярные выражения mod_rewrite

2.1 Совпадение буквальной строки

2.2 Один любой символ

2.3 Повторения

2.4 Анкоры (начало и конец строки)

2.5 Группировка (последовательность из нескольких заданных символов)

2.6 Обратные ссылки (подстановка найденного значения)

2.7 Класс символов (любой из перечисленных символов)

2.8 Количество повторений

2.9 ИЛИ (альтернатива)

2.10 НЕ (противоположность)

3. Флаги RewriteRule

4. Директива RewriteCond

5. Частые случаи и примеры использования mod_rewrite

6. Продвинутые техники

7. Директива RewriteMap

8. Директива RewriteOptions, технические подробности, когда НЕ использовать mod_rewrite


Запись директивы RewriteRule начинается с Шаблона – условного обозначения для которого проверяется, соответствует оно строке или нет. Этот шаблон представляет собой регулярное выражение.

Регулярное выражение – это условное обозначение требований, которым должна соответствовать искомая строка.


Совпадение буквальной строки

В только что созданной папке попробуйте открыть файл dogs.php, для меня это http://localhost/mr/dogs.php

Естественно, т.к. мы не создавали файл dogs.php, то файл отсутствует, и мы видим сообщение об ошибке, что такого файла нет.

Теперь в этой же директории в файл .htaccess скопируйте строки:

RewriteEngine on
RewriteRule dogs index.html

Опять пробую открыть адрес http://localhost/mr/dogs.php

Как видим, больше сообщения об ошибке нет. Нам показано содержимое файла index.html

В строке

RewriteRule dogs index.html
  • RewriteRule означает, что после неё будет следовать правило
  • dogs – это образец, который будет искаться в URL
  • index.html – это подстановка, новая строка, которая будет отправлена серверу.

Строка RewriteEngine on включает движок mod_rewrite. Если этого не сделать, то правила перезаписи не будут работать.

Как видим, если совпадение найдено, то начальный адрес полностью заменяется на новый.

Причём запрос необязательно должен быть в точносоти таким, как в Шаблоне, например, именно dogs (мы вводили dogs.php), достаточно, чтобы это сочетание встречалось в любом месте. Например, попробуем открыть адрес http://localhost/mr/catsdogsbirds.php – и мы вновь увидем index.html, поскольку в исходном запросе встречается последовательность dogs.

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

RewriteRule образец цель

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

В рассмотренном примере, в строке RewriteRule dogs index.html последовательность символов dogs является регулярным выражением. Как мы выяснили, этому регулярному выражению соответствуют строки «dogs», «dogs.php», «dogsandcats». Но этому регулярному выражению не соответствуют, например, строки «dog», «pudel», «mydog.php», «any_string» - вы можете убедиться в этом сами, пытаясь открыть эти страницы в веб-браузере.

Обратите внимание, что в строке браузера было введено, к примеру, http://localhost/mr/catsdogsbirds.php но поиск выполнялся только по части строки: часть http://localhost/mr/ отбрасывалась, и поиск Шаблона выполнялся в оставшейся части, а именно по строкам dogs.php и catsdogsbirds.php.


Если правило RewriteRule установлено в .htaccess, то анализируется только та часть запроса, которая относится к текущей директории. Итак, если файл .htaccess находится в папке /test/app1/ и к серверу поступает запрос http://localhost/mr/test/app1/requests/ajax.php, то путь до текущей папки, в том числе начальный слэш, будут отброшены. В результате Шаблон будет искаться в строке requests/ajax.php.

По умолчанию в качестве Подстановки также подразумевается относительный путь до файла. Более подробно о других вариантах Подстановки (URL адрес на другой сайт, абсолютные пути) будет рассказано позже.

Итак, мы уже начали использовать регулярные выражения mod_rewrite – и это было несложно! Но настоящую свою силу регулярные выражения раскрывают с подстановочными символами.

Один любой символ

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

К примеру, возьмём один из подстановочных символов регулярных выражений: . (точка)

. (точка) соответствует любому символу, но только одному

Регулярное выражение a.b означает: ищется любая строка, в которой в любом месте есть последовательность символов a, затем может быть любой символ, а затем символ b.

Поэтому регулярное выражение a.b  соответствует acb, axb, a@b, и т.д.

Также соответствует Decalb and Marbelized, поскольку в этих словах встречается строка, соответствующая a.b

Чтобы лучше понимать, запишите в файл .htaccess следующие строки

RewriteEngine on
RewriteRule a.b index.html

И попробуйте открывать адреса:

  • http://localhost/mr/acb
  • http://localhost/mr/axb
  • http://localhost/mr/a@b
  • http://localhost/mr/arbelized

Очевидно, что все они соответствуют шаблону a.b, а адрес, например, http://localhost/mr/ab вызовет ошибку, поскольку не будет соблюдено правило, что между символами a и b должен быть любой символ. Поскольку совпадение не найдено, то не будет делаться перезапись (не будет показана страница index.html), а вместо неё веб-серверу будет отправлен запрос показать страницу ab; но эта страница отсутствует, поэтому сервер выдаёт ошибку, что она не найдена.

Если нужно использовать буквальную точку, а не в её значении подстановочного символа, то тогда её нужно экранировать обратным слэшем:

RewriteRule "^index\.html$"  "welcome.html"

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

Повторения

Итак, в регулярных выражениях могут использоваться буквы, которые трактуются буквально. Например, регулярное «dog» означает искать строку, содержащую «dog». Также буквально трактуются цифры. К примеру, регулярное выражение «dog22» означает искать строку, содержащую последовательность «dog22».


Кроме буквальных символов, в регулярных выражениях применяются символы, которые имеют символическое (а не буквальное) значение. С одним из таких символов мы уже познакомились – это точка, которая обозначает любой символ. Теперь мы познакомимся со значением таких символов как + (знак плюс), * (звёздочка) и ? (знак вопроса). Эти символы в регулярных выражениях обозначают, сколько раз должен встречаться символ, стоящий прямо перед ними.

+ означает, что символ, который стоит перед этим символом, должен повториться один или более раз: «a+» соответствует «a», «aa», «aaa» и «Mondrian» (поскольку в этом слове a встречается хотя бы один раз)

* означает, что символ, который стоит перед этим символом, должен повториться ноль раз или более. «a*» соответствует «a», «aa» и пустой строке («»). На самом деле, этот пример соответствует чему угодно.

? означает, что символ, который стоит перед этим символом может повторяться ноль или один раз (т.е. является необязательным). «colou?r» соответствует «color» и «colour»

Анкоры (начало и конец строки)

По умолчанию заданное регулярное выражение ищется в любой части строки для анализа. Используя анкоры мы можем символически обозначить начало и (или) конец строки:

^ означает «начало строки». «^a» соответствует «alpha» и «Arnold»

$ означает «конец строки». «a$» соответствует «alpha» и «stella»

«^$» соответствует пустой строке

«^» соответствует любой строке (у любой строки есть начало)

В нашем примере, когда в качестве шаблона поиска указывалась строка «dogs», то для неё в том числе соответствовала строка catsdogsbirds.php. Если мы хотим сделать так, чтобы содержимое файла видели только те, кто открыл адрес только http://localhost/mr/dogs (а не, например, http://localhost/mr/catsdogsbirds.php), то тогда наши директивы будут выглядить так:

RewriteEngine on
RewriteRule ^dogs$ index.html

В регулярном выражении ^dogs$ мы использовали анкоры начала и конца строки, этому соответствует только адрес http://localhost/mr/dogs

Группировка (последовательность из нескольких заданных символов)

( ) , т.е. круглые скобки, позволяют вам группировать несколько символов, в результате модификаторы, например, повторители, будут применяться ко всему, что находится внутри скобок, а не к одному символу, который стоит перед модификатором.

«(ab)+» соответствует ababababababab

Обратные ссылки (подстановка найденного значения)

( ) круглые скобки не только группируют символы, они позволяют присваивать ту часть строки, которая соответствует выражению в скобках, переменным, для последующего их использования. Эти переменные называются «обратные ссылки». Они вызываются через $1 или %1 в зависимости от контекста. Можно использовать более чем одну пару скобок, тогда последующие обратные ссылки вызываются как $2 (или %2) и т.д.

Именно обратные ссылки имеют особое значение для ЧПУ. В предыдущих примерах, в случае совпадения строки, мы показывали пользователю другую страницу (index.html) вместо запрошенной им. И эта страница не получала никаких параметров от пользователя. Обратные ссылки позволяют нам передавать найденную строку новой странице.

Создайте новый файл index.php со следующим содержимым:


<?php

foreach($_GET as $key => $value)
{
   echo 'Получено:<br />';
   echo 'Переменная: ' . $key . '<br />';
   echo 'Значение: ' . $value;
   echo '<hr />';
}

Это PHP скрипт, который ищет переменные переданные методом GET и если они найдены показывает имя переменной и её значение. В противном случае скрипт ничего не выводит.

Вернёмся к файлу .htaccess, заменим его содержимое на:

RewriteEngine on
RewriteRule (dogs) index.php?page=$1

Откроем в браузере http://localhost/mr/dogs

Итак, мы видим, что сервер показал нам файл index.php. При этом скрипт получил переменную page, которой присвоено значение dogs, т.е. сервер получил строку index.php?page=dogs. Как мы видим, вместо $1 было подставлено то, что найдено в скобках.

Можно передавать сразу несколько значений, к примеру, запишем в файл .htaccess

RewriteEngine on
RewriteRule phones/(.*)/(.*)/(.*) index.php?vendor=$1&brand=$2&model=$3

И откроем ссылку http://localhost/mr/phones/Samsung/Galaxy/S100

Итак, мы задали регулярное выражение phones/(.*)/(.*)/(.*), оно обозначает: «искать строку, которая начинается с phones/, затем следует любая последовательность символов, затем следует /, затем следует любая последовательность символов, затем следует /, затем следует любая последовательность символов».

Из этой строки, то, что найдено в первых скобках подставляется в новый адрес вместо $1, то, что найдено во вторых скобках заменяется в новом адресе на $2, и то, что найдено в третьих скобках заменяется в новом адресе на $3.

Именно этот, довольно простой, механизм лежит в основе работы ЧПУ.

В зависимости от того, какую страницу запросил пользователь (какие данные содержаться в URL), ему можно показать различное содержимое.

Специальная переменная $0 содержит обратную ссылку ко всему совпавшему выражению.

Чтобы записать буквальный знак доллара, его нужно экранировать, используя обратный слеш. 

Итак, круглые скобки выполняют две функции:

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

Класс символов (любой из перечисленных символов)

[ ] определяет «класс символов».

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

[abc] соответствует a ИЛИ b ИЛИ c (но не всем сразу, а именно одному)

«c[uoa]t» соответствует cut, cot или cat. Также соответствует cote. Но не соответствует coat, поскольку символ из класса символов должен встречаться только один раз! Т.е. регулярное выражение c[uoa]t означает: искать строку, которая начинается с символа c, за которым идёт только один из любых символов u, o, a, а затем следует символ t.

Класс символов можно задавать с помощью сокращённых обозначений:

  • [A-Z] – обозначает все заглавные буквы латинского алфавита
  • [a-z] – обозначает все строчные буквы латинского алфавита
  • [0-9] – обозначает все цифры

Можно группировать классы символов, например [A-Za-z0-9] обозначает все заглавные и строчные буквы, а также цифры.

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

После квадратных скобок класса символа можно указать количество, сколько раз должен встречаться символ из класса символов.

К примеру, [A-Z] обозначает любую ОДНУ заглавную букву. А [A-Z]+ обозначает строку из заглавных букв ЛЮБОЙ ДЛИНЫ. Знак плюс, как мы узнали чуть раньше, устанавливает, что стоящий перед ним символ должен встречаться 1 или более раз.

Вернёмся к нашему предыдущему примеру и перезапишем наше регулярное выражение phones/(.*)/(.*)/(.*). В имени вендора, а также в бренде могут присутствовать только большие и маленькие буквы, а в модели могут присутствовать большие и маленькие буквы, а также цифры, поэтому получаем:

RewriteEngine on
RewriteRule phones/([A-Za-z]+)/([A-Za-z]+)/([A-Za-z0-9]+) index.php?vendor=$1&brand=$2&model=$3

И откроем ссылку http://localhost/mr/phones/Samsung/Galaxy/S100

Количество повторений

{ } (фигурные скобки) используются для выражения минимального и максимального числа требуемых соответствий. Они могут задаваться четырьмя различными способами:

Спецификатор Значение
{n} Соответствие предыдущего элемента, если он встречается ровно n раз.
{n,m} Соответствие предыдущего элемента, если он встречается по меньшей мере n раз, но не более чем m раз.
{n,} Соответствие предыдущего элемента, если он встречается n или более раз.
{,m} Соответствие предыдущего элемента, если он встречается не более чем m раз.

Запишем в файл .htaccess:

RewriteEngine on
RewriteRule ^.{4}$ index.html

Регулярное выражение ^.{4}$ соответствует строке из любых символов, длиной ровно четыре символа. Анкоры ^ и $ указывают на начало и конец строки, символ . (точка) означает любой символ, а {4} говорит о том, что этот символ должен встречаться ровно четыре раза.

Т.е. при открытии ссылки http://localhost/mr/dogs будет показано содержимое файла index.html, а при открытии, например, ссылки http://localhost/mr/dog или http://localhost/mr/dogsandcats, совпадений не будет.

Можно уточнить и указать, что четыре раза должна встречаться любая прописная буква:

RewriteEngine on
RewriteRule ^[a-z]{4}$ index.html

ИЛИ (альтернатива)

| (символ «труба») означает «или», т.е. строка считается совпадающей, если в ней присутствует символ до трубы или после

a|b означает a или b

С помощью скобок можно группировать символы

(good)|(bad) соответствует good, bad, goods, badness, также соответствует goodbad (не смотря на то, что должно быть только что-то одно, в данной строке встречается строка good, что уже достаточно, также достаточно того, если встречается строка bad).

Для примера в файле .htaccess:

RewriteEngine on
RewriteRule ^(dogs)|(cats)$ index.html

Такое правило соответствует только адресам http://localhost/mr/dogs и http://localhost/mr/cats

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

НЕ (противоположность)

В регулярных выражения mod_rewrite, ! (восклицательный знак) отрицает (инвертирует) любое соответствие.

В mod_rewrite, символ НЕ ('!') доступен как возможный префикс Шаблона (т.е. ставится перед шаблоном). Это даёт вам возможность отвергать Шаблон; это имеет примерно следующий смысл: «если текущий URL НЕ совпадает с этим шаблоном». Это может использоваться для исключительных случаев, где проще сопоставлять с отрицающим Шаблоном, или в качестве последнего дефолтного правила.

Помните: когда используете символ НЕ для отрицания шаблона, вы не можете включить в этом шаблон группирующие части подстановочных символов. Это потому, что когда шаблон НЕ совпадает (отрицание совпадений), то отсутствует содержимое для групп. Таким образом, если используется отрицающий шаблон, вы не можете использовать $N (обратные ссылки) в подстановочной строке!

В классе символов, инвертирующим символом является ^

[^ab] соответствует любому символу, кроме a или b.

Подытожим наши знания о регулярных выражениях:

Символ Значение Пример
последовательность символов Совпадает со строкой, в которой имеется данная последовательность символов dog будет совпадать с dog, dogs, mydogs
. Совпадает с любым одним символом c.t будет совпадать с catcotcut и т.д..
+ Повторение предыдущего совпадения один или более раз a+ совпадает с aaaaaa и т.д.
* Повторение предыдущего совпадения ноль или более раз a* совпадает со всеми строками, с которыми совпадает a+, но также совпадает с любой пустой строкой.
? Делает совпадение опциональным colou?r будет совпадать с color с colour.
^ Называется анкором, совпадает с началом строке ^a совпадает со строкой, начинающейся с a
$ Это другой анкор, он совпадает с концом строки a$ совпадает со строками, которые кончаются на a.
( ) Группировка нескольких символов в один юнит, а также захватывает совпадение для использования в обратных ссылкахbackreference. (ab)+ совпадает с ababab - т.е. + применяется к группе.
[ ] Класс символов - совпадение с одним из указанных символов c[uoa]t совпадает с cutcot или cat.
{ } Количество повторений: можно указать точное количество, диапазон, либо минимальное/максимальное количество повторений предыдущего юнита (символ, группа символов, символа из класса символов) ^.{4}$ совпадает со строкой dogs, cats
| Альтернатива - совпадает символ (группа символов) до трубы или после трубы (good)|(bad) совпадает со строками good, bad, goods
! Противоположность - совпадение засчитывается, если НЕТ соответствия !^a совпадает с любой строкой, НЕ начинающейся с a
[^ ] Негативный набор символов - совпадает любой символ, который не указан c[^/]t совпадает с cat или c=t но не с c/t

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

Продолжение: «Полное руководство по mod_rewrite (часть 3): Флаги RewriteRule».


Рекомендуется Вам:

8 комментариев to Полное руководство по mod_rewrite (часть 2): Регулярные выражения

  1. Алексей:

    Теперь в этой же директории в файл .htaccess скопируйте строки:
    RewriteEngine on
    RewriteRule dogs index.html
    Опять пробую открыть адрес http://localhost/mr/dogs.php
    Как видим, больше сообщения об ошибке нет. Нам показано содержимое файла index.html
    В строке
    RewriteRule dogs index.php
    RewriteRule означает, что после неё будет следовать правило
    dogs – это образец, который будет искаться в URL
    index.php – это подстановка, новая строка, которая будет отправлена серверу.

    видимо, ошибка. Надо php иправить на html?

  2. Алексей:

    Создайте новый файл index.php со следующим содержимым:

    <?php
     
    foreach($_GET as $key => $value)
    {
       echo 'Получено:<br />';
       echo 'Переменная: ' . $key . '<br />';
       echo 'Значение: ' . $value;
       echo '<hr />';
    }

    у меня без "?>" не заработало..

    сижу, вникаю просто.

    • Alexey:

      У вас какая-то ошибка или просто пустой вывод?

      Закрывающий тэг «?>» стал необязательным с определённой версии PHP, возможно, у вас предыдущая версия. Также если это не конец файла, а за PHP кодом следует, например, HTML, то закрывающий тэг тоже нужен.

      В современных версиях, если это конец файла, разницы есть закрывающий тэг «?>» или его нет, быть не должно.

      • Алексей:

        была ошибка 500.

        Возможно, сервер или браузер кэшируют что-то где-то. Сначала без ?> не работалао. Потом с ним заработало. Теперь работает и так, и так. Тренируюсь на хостинге. Версия  Apache 2.4 + PHP 7.4 opcache

        В любом случае Спасибо за мануал. Просветляет.

  3. Mitrios:

    > Такое правило соответствует только адресам http://localhost/mr/dogs и http://localhost/mr/cats

    а разве http://localhost/mr/dogscats не сработает?

    • Alexey:

      Нет, не сработает из-за символов ^ и $, которые означают начало и конец строки. Чтобы было понятнее, это правило можно записать как два регулярных выражения ^dogs$ и ^cats$.

  4. ETK:

    Вопрос: 

    есть строка адресная вида: http://localhost/m/param1/?param2=1&param3=4…..

    по факту это:  http://localhost/m.html?param1=param1&param2=1&param3=4…..

    "?param2=1&param3=4….." динамика, как в php получить $_GET['param2'] и т.д.

    Можно как то комбинировать всё это?

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *