Regex тестер (PCRE)
Перевірити регулярний вираз саме так, як це зробить PHP
Дані надсилаються на сервер, але не зберігаються
Цей інструмент не можна виконати у браузері, тому введені дані надсилаються на наш сервер. Ми обробляємо їх у пам'яті та одразу повертаємо результат: нічого не записується в базу, у файли чи в логи, нічого не передається третім сторонам. Та все ж не вставляйте сюди справжні робочі паролі чи ключі - для чутливих даних краще скористатися локальними інструментами.
Чому цей тестер працює на сервері
Більшість онлайн-тестерів регулярних виразів виконують ваш патерн у JavaScript. Для PHP-розробника це пастка: PCRE і JavaScript - різні рушії, і вираз, який зеленіє в браузері, може повестися інакше в preg_match(). Тому цей інструмент надсилає патерн на сервер і виконує його справжніми функціями PHP.
Конструкції, які розуміє PCRE і не розуміє (або трактує інакше) JavaScript:
- Присвійні квантифікатори -
a++,\d*+ - Атомарні групи -
(?>aa|a) - Рекурсія -
(?R), якою розбирають вкладені дужки - Іменовані групи у стилі Python -
(?P<name>...) - Модифікатор
x- пробіли й коментарі всередині виразу - POSIX-класи -
[[:alpha:]],[[:digit:]] - Відносні зворотні посилання -
\g{-1} - Змінна довжина в lookbehind -
(?<=ab|abc)
Модифікатор u і кирилиця
Найчастіша помилка в українських проєктах. Без модифікатора u PCRE працює побайтово, а не посимвольно, і \w, . та \b перестають розуміти кирилицю:
preg_match_all('/\w+/', 'привіт світ', $m); // 0 збігів
preg_match_all('/\w+/u', 'привіт світ', $m); // 2 збіги
preg_match('/^.{6}$/', 'привіт'); // false - це 12 байтів
preg_match('/^.{6}$/u', 'привіт'); // true - це 6 символів
Правило просте: якщо у тексті може бути кирилиця - додавайте u. Спробуйте обидва варіанти у формі вище, різниця видно одразу.
Три режими
// Один збіг: чи підходить рядок і що саме збіглося
if (preg_match('/^\+380\d{9}$/', $phone)) { /* ... */ }
// Усі збіги: витягнути всі входження
preg_match_all('/#(\w+)/u', $text, $matches);
$hashtags = $matches[1];
// Заміна: $1, $2 посилаються на групи
$slug = preg_replace('/[^a-z0-9]+/i', '-', $title);
Іменовані групи
Читабельніше за $matches[3] через півроку після написання коду:
preg_match(
'/(?P<user>[\w.]+)@(?P<host>[\w.]+)/',
'пишіть на bob@example.com',
$m,
);
$m['user']; // bob
$m['host']; // example.com
Катастрофічний відкат
Вираз на кшталт /(a+)+$/ на рядку з багатьох a і однієї зайвої літери змушує рушій перебирати експоненційну кількість варіантів. Це класична вразливість ReDoS: один запит вантажить процесор на секунди.
PCRE має запобіжник - pcre.backtrack_limit. Вичерпавши ліміт, вона повертає false, а не зависає. Але preg_match() поверне false і при синтаксичній помилці, тому мовчазна перевірка через if (preg_match(...)) приховує обидві біди:
$result = preg_match($pattern, $subject);
if ($result === false) {
// Це не «збігів немає», це збій
logger()->warning('Regex failed: ' . preg_last_error_msg());
}
Цей інструмент навмисно виставляє нижчий ліміт відкатів, ніж стандартний. Якщо ви побачили тут помилку про вичерпаний ліміт - вираз варто спростити, бо на проді він поведеться так само.
У Laravel
// Валідація: правило regex бере патерн цілком, з роздільниками
$request->validate([
'phone' => ['required', 'regex:/^\+380\d{9}$/'],
'slug' => ['required', 'regex:/^[a-z0-9\-]+$/'],
]);
// Хелпери Str, коли не хочеться писати preg_ руками
Str::match('/id=(\d+)/', $url); // перший збіг групи
Str::isMatch('/^admin/', $role); // булеве
Str::replaceMatches('/\s+/u', ' ', $text);
// Маршрути
Route::get('/blog/{slug}', ...)->where('slug', '[a-z0-9\-]+');
- Де виконується
- на сервері
- Категорія
- Текст