Цапля

Редирект и регулярные выражения

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

Глава первая. Для таких, как я

Регулярные выражения — мощный инструмент для операций с текстовой информацией. Они помогают сформулировать некое правило для текста, которое будет использоваться при поиске или замене нужных фрагментов текста. Например, при валидации заполнения формы пользователем, внутри поискового алгоритма или в .htaccess для составления правил о том какие адреса на какие надо заменять.

  • htaccess.madewithlove.be/ — инструмент для проверки правильности регулярных выражений в .htaccess;
  • it-simple.ru/?p=2109 — хорошая статья по регулярным выражениям и очень полезные ссылки на другие статьи по теме, которые дают более полное представление о регулярных выражениях;
  • gskinner.com/RegExr/ — сервис проверки работы регулярных выражений с подсказками по синтаксису;
  • htaccessredirect.net — сервис генерации некоторых правил для .htaccess;
  • habrahabr.ru/company/sprinthost/blog/129560/ — некоторые подробности из жизни mod_rewrite.

Некоторые функции для работы со строками

if (strpos($string, $substring) !== false) {
    echo 'match is found';
	} else {
	    echo 'no matches are found'; };

strpos() возвращает первую позицию в строке ($string), в которой была найдена заданная подстрока ($substring). Это значит, что если строка начинается с заданной подстроки, то strpos() вернет 0, так как это значение начальной позиции искомой подстроки. strpos() возвращает так же 0, если искомая подстрока не найдена. Чтобы избежать такой двусмысленности ноля используются операторы сравнения === или !==. $a===$b возвращает true, если $a равно $b И имеет тот же тип. $a!==$b возвращает true, если $a НЕ равно $b ИЛИ они НЕ одинакового типа.

str_replace ($substring, $new_substring , $string [, int &$count ]) — в квадратных скобках указаны необязательные параметры.

Выполняет поиск подстроки ($substring) в строке ($string) и замену подстроки на другую заданную подстроку ($new_substring). Если указан параметр count, то число замен будет ограничено значением этого параметра.

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

Некоторые функции для работы с текстовыми шаблонами

В php поддерживаются два стандарта регулярных выражений: POSIX и PCRE(Perl-compatible regular expressions), которые отличаются синтаксисом и некоторыми возможностями. PCRE является более мощным инструментом для работы с текстовыми паттернами, поскольку дает возможность реализовать и жадный, и нежадный поиск, рекурсивное задание шаблонов и некоторые другие вещи.

preg_match ($pattern, $subject[, &$matches[, $flags = 0[, $offset = 0 ]]]) — в квадратных скобках указаны необязательные параметры.

preg_match(для PCRE) возвращает количество вхождений шаблона($pattern) в выражение($subject).

preg_replace ($pattern, $replacement, $subject[, $limit=-1[, &$count]])

preg_replace(для PCRE) выполняет поиск совпадений в строке($subject) с шаблоном ($pattern) и заменяет их на заданное выражение($replacement).

ereg($pattern, $string[, &$regs ]) (для регулярных выражений POSIX)

ereg_replace ($pattern, $replacement, $string) (для регулярных выражений POSIX)

Глава вторая. Практическая задача

На одном из моих сайтов (работающем на WordPress) я создала родительскую категорию для трех уже существоваших категорий. Внутренние ссылки сделались правильные и красивые — никаких проблем. А вот, внешние ссылки, которые вели на старые адреса, остались рабочими. Конечно, это вполне удобно посетителям. Но я сочла будет еще лучше, если те, кто перешел по старым ссылкам, все же будут перенаправлены на новые адреса страниц. Не говоря уже о том, что в Яндекс.Вебмастере висели в проиндексированных оба адреса, а в выдаче был старый. Понадобился легендарный 301 редирект. Польза его воспета на бесчисленном количестве интернет-страниц, а три стандартные строчки для склейки домена с www и без обязательно включены в любой сео-блог. Но мою ситуацию все эти сео-секреты спасти никак не могли.

Шаблон формирования пермалинков на сайте: /%category%/%postname%/ Поэтому и старые и новые адреса вложенных категорий и записей в них полностью соответствовали этому шаблону. Необходимо было сделать два вида перенаправлений:

  • domain.com/category/child/ → domain.com/category/parent/child/
  • domain.com/child/post-name/ → domain.com/parent/child/post-name/

Когда я разберусь, почему .htaccess оставался холоден ко всем правилам, которые я пыталась установить, наверное созреет новый пост для блога. Поэтому редиректы были сделаны в functions.php, и этот способ опишу.

С strpos() все достаточно просто и понятно, незачем использовать регулярные выражения там, где можно обойтись меньшим. Разберемся с выражением: . Сделаем из этого выражения подопытный экземпляр, чтобы разобраться с любыми возможными вопросами, которые могут возникнуть при взгляде на выражение.

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

Что такое ? Это отрицательное утверждение о том, что подстроке /child/(.*) не предшествует /parent. Это нужно для того, чтобы исключить зацикливание. То есть чтобы не было переадресаций: domain.com/parent/child/post-name/domain.com/parent/parent/child/post-name/ → …

Почему в конце нет слеша: ? Потому что, во-первых, границы шаблона обозначены #. Значит как ограничитель шаблона слеш здесь не нужен. Во-вторых, внешняя ссылка на страничку может быть и со слешом на конце, и без него. Поэтому мы берем в подмаску последовательность любых символов.

Почему $1, а не $2? Потому что утверждения (например, (?=parent) или (?!parent)) не учитываются в качестве подмаски.