Хитрости при работе с регулярными выражениями PCRE на PHP

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

Никогда не используйте регулярные выражения

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

Например, регулярка для валидации телефонного номера может выглядеть примерно так:

^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$

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

Визуальные редакторы регулярных выражений

Используйте, например: Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript. Просто попробуйте и сами всё поймёте.

Используйте именованные подмаски

Используйте (?<name>pattern), (?’name’pattern) или (?P<name>pattern) вместо (pattern). В результате чего функции preg_match и preg_match_all будут добавлять в возвращаемый массив дополнительные элементы с именами ключей указанных в подмасках.

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

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

array(3) {
 [0] =>
 array(1) {
 [0] =>
 string(2) "45"
 }
 'afterfour' =>
 array(1) {
 [0] =>
 string(1) "5"
 }
 [1] =>
 array(1) {
 [0] =>
 string(1) "5"
 }
}

После чего можно применить к результату работы регулярного выражения:

$item = array_filter($item , 'is_string', ARRAY_FILTER_USE_KEY);

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

array(1) {
 'afterfour' =>
 array(1) {
 [0] =>
 string(1) "5"
 }
}

На этом всё. Если знаете ещё какие-либо хитрости — пишите в комментарии!