Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас

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\-]+');