Не так давно один из участников моего форума обнаружил ошибку у меня на сайте. Если ввести " / " в форму поиска, то выдавалась ошибка. Вроде бы ничего серьёзного, но это не очень хорошо. Поэтому эту ошибку я сразу нашёл и исправил. А ошибка была в том, что я кое-что забыл учесть. Это немного связано с экранированием...

Экранирование регулярного выражения в PHP

Не так давно один из участников моего форума обнаружил ошибку у меня на сайте. Если ввести "/" в форму поиска, то выдавалась ошибка. Вроде бы ничего серьёзного, но это не очень хорошо. Поэтому эту ошибку я сразу нашёл и исправил. А ошибка была в том, что я кое-что забыл учесть. Это немного связано с экранированием регулярных выражений на PHP, и давайте разберём эту тему подробнее.

Любое регулярное выражение, которое составляется из того, что прислал пользователь, всегда надо пропускать через функцию preg_quote(). Данная функция добавляет обратный слэш перед каждым служебным символом, например, перед символом "*".

Зачем это нужно? Давайте рассмотрим такой пример:

<?php
  $str = "abc * abc"; // Эта строка пришла от пользователя, например, методом POST
  preg_match("/$str/", $text); // Поиск соответствия в некой переменной $text
?>

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

И данная проблема решается с помощью функции preg_quote():

<?php
  $str = "abc * abc"; // Эта строка пришла от пользователя, например, методом POST
  $str = preg_quote($str); // Экранируем служебные символы
  preg_match("/$str/", $text); // Поиск соответствия в некой переменной $text
?>

Теперь "*" будет заменена на "\*", и никаких проблем при поиске соответсвия не возникнет, так как теперь "*" - это не более, чем часть текста, а не спецсимвол.

Вот на этом этапе я не ошибался, и всё сделал правильно, но я не учёл ещё кое-что:

<?php
  $str = "abc / abc"; // Эта строка пришла от пользователя, например, методом POST
  $str = preg_quote($str); // Экранируем служебные символы
  preg_match("/$str/", $text); // Поиск соответствия в некой переменной $text
?>

После подстановки регулярное выражение станет таким: "/abc / abc/". Как Вы понимаете, оно работать не будет, и PHP выдаст ошибку.

Вот эту-то ошибку и обнаружил один из посетителей данного сайта. Решается она очень просто: все прямые слэши экранируются отдельно. Если Вы используете другой символ границы регулярного выражения (например, "#"), то надо экранировать его. Итак, вот правильный код:

<?php
  $str = "abc / abc"; // Эта строка пришла от пользователя, например, методом POST
  $str = preg_quote($str); // Экранируем служебные символы
  $str = str_replace("/", "\/", $str); // Экранируем прямой слэш
  preg_match("/$str/", $text); // Поиск соответствия в некой переменной $text
?>

Вот и всё решение проблемы. Давайте ещё раз подведём итог, что Вы должны делать:

  1. Экранировать регулярное выражение с помощью функции preg_quote().
  2. Экранировать символ границы регулярного выражения с помощью функции str_replace().

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