Глава 1.5: Строки и регулярные выражения
Манипуляции со строками, поиск, замена, валидация
Почему строки так важны?
Строки — это 90% данных в веб-разработке:
┌─────────────────────────────────────────────────────────────────┐
│ ГДЕ ИСПОЛЬЗУЮТСЯ СТРОКИ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📝 Ввод пользователя Формы, комментарии, поиск │
│ 🌐 URL и маршруты /users/123/edit │
│ 📧 Email и сообщения Шаблоны писем, уведомления │
│ 🗄️ Данные из БД Практически всё — строки │
│ 📋 JSON/XML/HTML Парсинг и генерация │
│ 🔒 Безопасность Валидация, санитизация, экранирование│
│ 📊 Форматирование Даты, деньги, телефоны │
│ │
└─────────────────────────────────────────────────────────────────┘ЧАСТЬ 1: ОСНОВЫ СТРОК
1. Создание строк
Четыре способа
php
<?php
// 1. Одинарные кавычки — литеральная строка
$str1 = 'Привет, мир!';
$str2 = 'Это стоит $100'; // $100 — просто текст
$str3 = 'It\'s a string'; // Экранирование апострофа
// 2. Двойные кавычки — с интерполяцией
$name = "Иван";
$str4 = "Привет, $name!"; // Привет, Иван!
$str5 = "Привет, {$name}!"; // То же, явный синтаксис
$str6 = "Табуляция:\tНовая строка:\n"; // Спецсимволы работают
// 3. Heredoc — многострочная с интерполяцией
$html = <<<HTML
<div class="card">
<h1>$name</h1>
<p>Добро пожаловать!</p>
</div>
HTML;
// 4. Nowdoc — многострочная без интерполяции
$code = <<<'CODE'
<?php
$x = 5;
echo $x; // $x останется как текст
CODE;Сравнение кавычек
┌──────────────────┬────────────────────┬────────────────────┐
│ │ Одинарные '' │ Двойные "" │
├──────────────────┼────────────────────┼────────────────────┤
│ Интерполяция $var│ ❌ Нет │ ✅ Да │
│ Спецсимволы \n │ ❌ Только \' и \\ │ ✅ Все │
│ Производительность│ Чуть быстрее │ Чуть медленнее │
│ Использование │ Без переменных │ С переменными │
└──────────────────┴────────────────────┴────────────────────┘Специальные символы (только в двойных кавычках)
php
<?php
$str = "Первая строка\nВторая строка"; // \n — новая строка
$str = "Колонка1\tКолонка2"; // \t — табуляция
$str = "Путь: C:\\folder\\file"; // \\ — обратный слэш
$str = "Он сказал: \"Привет!\""; // \" — кавычка
$str = "Цена: \$100"; // \$ — знак доллара
$str = "Символ: \x41"; // \x41 — hex код (A)
$str = "Unicode: \u{1F600}"; // \u{} — Unicode (😀)Интерполяция переменных
php
<?php
$name = "Иван";
$user = ["name" => "Мария", "age" => 25];
$obj = new stdClass();
$obj->name = "Пётр";
// Простая переменная
echo "Привет, $name!"; // Привет, Иван!
// Элемент массива — нужны {}
echo "Имя: {$user['name']}"; // Имя: Мария
echo "Имя: $user[name]"; // Работает, но не рекомендуется
// Свойство объекта
echo "Имя: {$obj->name}"; // Имя: Пётр
echo "Имя: $obj->name"; // Тоже работает
// Выражения — нужен трюк
$x = 5;
echo "Результат: {${''} . ($x * 2)}"; // Не работает!
echo "Результат: " . ($x * 2); // Используй конкатенациюКонкатенация
php
<?php
$first = "Hello";
$second = "World";
// Оператор .
$result = $first . " " . $second; // "Hello World"
// Оператор .=
$str = "Hello";
$str .= " ";
$str .= "World"; // "Hello World"
// Что лучше: конкатенация или интерполяция?
$name = "Иван";
// Интерполяция — для простых случаев (читабельнее)
$greeting = "Привет, $name!";
// Конкатенация — для сложных выражений
$greeting = "Привет, " . ucfirst(strtolower($name)) . "!";2. Базовые функции строк
Длина строки
php
<?php
$str = "Привет";
// strlen — количество БАЙТ (не символов!)
echo strlen($str); // 12 (UTF-8: кириллица = 2 байта на символ)
echo strlen("Hello"); // 5
// mb_strlen — количество СИМВОЛОВ (для UTF-8)
echo mb_strlen($str); // 6
echo mb_strlen($str, 'UTF-8'); // 6 (явное указание кодировки)
// Всегда используй mb_* для кириллицы!Изменение регистра
php
<?php
$str = "Привет Мир";
// Стандартные (только ASCII!)
echo strtolower("HELLO"); // "hello"
echo strtoupper("hello"); // "HELLO"
echo ucfirst("hello world"); // "Hello world"
echo ucwords("hello world"); // "Hello World"
echo lcfirst("Hello"); // "hello"
// Для UTF-8 — используй mb_*
echo mb_strtolower("ПРИВЕТ"); // "привет"
echo mb_strtoupper("привет"); // "ПРИВЕТ"
echo mb_convert_case("привет", MB_CASE_TITLE); // "Привет"
// ucfirst для UTF-8 (своя функция)
function mb_ucfirst(string $str): string {
return mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 1);
}
echo mb_ucfirst("привет"); // "Привет"Обрезка пробелов
php
<?php
$str = " Привет ";
trim($str); // "Привет" — с обеих сторон
ltrim($str); // "Привет " — слева (left)
rtrim($str); // " Привет" — справа (right)
// Можно указать символы для удаления
$str = "...Привет...";
trim($str, "."); // "Привет"
$str = "Hello World!!!";
rtrim($str, "!"); // "Hello World"
// Удаление переносов строк
$str = "Текст\r\n";
rtrim($str, "\r\n"); // "Текст"Подстрока
php
<?php
$str = "Hello World";
// substr(строка, начало, длина)
echo substr($str, 0, 5); // "Hello"
echo substr($str, 6); // "World" (до конца)
echo substr($str, -5); // "World" (с конца)
echo substr($str, 0, -6); // "Hello" (без последних 6)
echo substr($str, -5, 3); // "Wor"
// Для UTF-8
$str = "Привет Мир";
echo mb_substr($str, 0, 6); // "Привет"
echo mb_substr($str, -3); // "Мир"3. Поиск в строках
Поиск позиции
php
<?php
$str = "Hello World, Hello PHP";
// strpos — первое вхождение (регистрозависимо)
echo strpos($str, "Hello"); // 0
echo strpos($str, "World"); // 6
echo strpos($str, "hello"); // false (не найдено!)
echo strpos($str, "o"); // 4
// ВАЖНО: проверка результата
if (strpos($str, "Hello") !== false) { // !== а не !=
echo "Найдено!";
}
// ❌ Неправильно (0 == false)!
if (strpos($str, "Hello")) {
echo "Это не сработает для позиции 0!";
}
// stripos — без учёта регистра
echo stripos($str, "hello"); // 0
// strrpos — последнее вхождение
echo strrpos($str, "Hello"); // 13
// Поиск с позиции
echo strpos($str, "Hello", 1); // 13 (начать поиск с позиции 1)
// Для UTF-8
$str = "Привет Мир";
echo mb_strpos($str, "Мир"); // 7Проверка наличия подстроки
php
<?php
$str = "Hello World";
// PHP 8+ — str_contains (рекомендуется!)
if (str_contains($str, "World")) {
echo "Содержит 'World'";
}
// PHP 8+ — str_starts_with, str_ends_with
if (str_starts_with($str, "Hello")) {
echo "Начинается с 'Hello'";
}
if (str_ends_with($str, "World")) {
echo "Заканчивается на 'World'";
}
// До PHP 8 — через strpos
function contains(string $haystack, string $needle): bool {
return strpos($haystack, $needle) !== false;
}
function startsWith(string $haystack, string $needle): bool {
return strpos($haystack, $needle) === 0;
}
function endsWith(string $haystack, string $needle): bool {
return substr($haystack, -strlen($needle)) === $needle;
}Подсчёт вхождений
php
<?php
$str = "Hello World, Hello PHP, Hello Universe";
echo substr_count($str, "Hello"); // 3
echo substr_count($str, "o"); // 44. Замена в строках
str_replace — простая замена
php
<?php
$str = "Hello World";
// Замена одной подстроки
echo str_replace("World", "PHP", $str); // "Hello PHP"
// Замена нескольких (массивы)
$search = ["Hello", "World"];
$replace = ["Привет", "Мир"];
echo str_replace($search, $replace, $str); // "Привет Мир"
// Замена на одно значение
echo str_replace(["a", "e", "i", "o", "u"], "*", "Hello World");
// "H*ll* W*rld"
// Подсчёт замен
$count = 0;
$result = str_replace("o", "0", "Hello World", $count);
echo $count; // 2
// Без учёта регистра
echo str_ireplace("hello", "Привет", "HELLO World"); // "Привет World"strtr — замена по карте
php
<?php
// Замена символов
$str = "Hello";
echo strtr($str, "el", "ip"); // "Hippo" (e→i, l→p)
// Замена по массиву (более гибко)
$trans = [
"Hello" => "Привет",
"World" => "Мир"
];
echo strtr("Hello World", $trans); // "Привет Мир"
// Транслитерация
$translit = [
"а" => "a", "б" => "b", "в" => "v", "г" => "g", "д" => "d",
"е" => "e", "ё" => "yo", "ж" => "zh", "з" => "z", "и" => "i",
"й" => "y", "к" => "k", "л" => "l", "м" => "m", "н" => "n",
"о" => "o", "п" => "p", "р" => "r", "с" => "s", "т" => "t",
"у" => "u", "ф" => "f", "х" => "h", "ц" => "ts", "ч" => "ch",
"ш" => "sh", "щ" => "sch", "ъ" => "", "ы" => "y", "ь" => "",
"э" => "e", "ю" => "yu", "я" => "ya", " " => "-"
];
$str = "Привет мир";
echo strtr(mb_strtolower($str), $translit); // "privet-mir"substr_replace — замена по позиции
php
<?php
$str = "Hello World";
// Заменить часть строки
echo substr_replace($str, "PHP", 6); // "Hello PHP"
echo substr_replace($str, "PHP", 6, 5); // "Hello PHP" (заменить 5 символов)
echo substr_replace($str, "Dear ", 0, 0); // "Dear Hello World" (вставка)
echo substr_replace($str, "", 5); // "Hello" (удаление с позиции)
// Маскирование
$card = "4111111111111111";
$masked = substr_replace($card, str_repeat("*", 12), 0, 12);
// "************1111"5. Разделение и объединение
explode — строка в массив
php
<?php
$str = "apple,banana,orange";
$arr = explode(",", $str);
// ["apple", "banana", "orange"]
// С лимитом
$arr = explode(",", $str, 2);
// ["apple", "banana,orange"]
// Отрицательный лимит (PHP 5.4+)
$arr = explode(",", $str, -1);
// ["apple", "banana"] — без последнего
// Разделение по пробелам
$str = "Hello World PHP";
$words = explode(" ", $str);
// ["Hello", "World", "PHP"]
// Обработка CSV-строки
$csv = "Иван,25,Москва";
[$name, $age, $city] = explode(",", $csv);implode / join — массив в строку
php
<?php
$arr = ["apple", "banana", "orange"];
echo implode(", ", $arr); // "apple, banana, orange"
echo implode("-", $arr); // "apple-banana-orange"
echo implode("", $arr); // "applebananaorange"
// join — алиас implode
echo join(", ", $arr); // "apple, banana, orange"
// Практика: формирование SQL
$ids = [1, 2, 3, 4, 5];
$sql = "SELECT * FROM users WHERE id IN (" . implode(",", $ids) . ")";
// SELECT * FROM users WHERE id IN (1,2,3,4,5)
// Практика: хлебные крошки
$breadcrumbs = ["Главная", "Каталог", "Телефоны"];
echo implode(" → ", $breadcrumbs);
// "Главная → Каталог → Телефоны"str_split — строка в массив символов
php
<?php
$str = "Hello";
$chars = str_split($str);
// ["H", "e", "l", "l", "o"]
// По N символов
$chunks = str_split($str, 2);
// ["He", "ll", "o"]
// Для UTF-8
$str = "Привет";
$chars = mb_str_split($str); // PHP 7.4+
// ["П", "р", "и", "в", "е", "т"]
// До PHP 7.4
$chars = preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY);wordwrap и chunk_split
php
<?php
// wordwrap — перенос по словам
$str = "Очень длинный текст, который нужно разбить на строки определённой длины";
echo wordwrap($str, 30, "\n", true);
/*
Очень длинный текст, который
нужно разбить на строки
определённой длины
*/
// chunk_split — разбить на части
$str = "1234567890";
echo chunk_split($str, 3, "-"); // "123-456-789-0-"6. Форматирование
sprintf — форматированные строки
php
<?php
// Основной синтаксис: %[позиция$][флаги][ширина][.точность]спецификатор
// Спецификаторы
$str = sprintf("Строка: %s", "текст"); // "Строка: текст"
$str = sprintf("Целое: %d", 42); // "Целое: 42"
$str = sprintf("Дробное: %f", 3.14159); // "Дробное: 3.141590"
$str = sprintf("Дробное: %.2f", 3.14159); // "Дробное: 3.14"
$str = sprintf("Процент: %%"); // "Процент: %"
// Ширина и выравнивание
$str = sprintf("|%10s|", "test"); // "| test|" — справа
$str = sprintf("|%-10s|", "test"); // "|test |" — слева
$str = sprintf("|%010d|", 42); // "|0000000042|" — нули
$str = sprintf("|%'#10s|", "test"); // "|######test|" — свой символ
// Позиционные аргументы
$str = sprintf("%2\$s %1\$s", "World", "Hello"); // "Hello World"
// Практика: цены
function formatPrice(float $price): string {
return sprintf("%01.2f ₽", $price);
}
echo formatPrice(1234.5); // "1234.50 ₽"
// Практика: ID с ведущими нулями
$id = 42;
echo sprintf("ORDER-%05d", $id); // "ORDER-00042"number_format — форматирование чисел
php
<?php
$num = 1234567.891;
echo number_format($num); // "1,234,568" (округление!)
echo number_format($num, 2); // "1,234,567.89"
echo number_format($num, 2, ',', ' '); // "1 234 567,89" (русский формат)
echo number_format($num, 2, '.', ''); // "1234567.89" (без разделителя тысяч)
// Функция для цен
function price(float $amount, string $currency = '₽'): string {
return number_format($amount, 2, ',', ' ') . ' ' . $currency;
}
echo price(1234567.89); // "1 234 567,89 ₽"printf — вывод форматированной строки
php
<?php
// printf = sprintf + echo
printf("Имя: %s, Возраст: %d\n", "Иван", 25);
// Выводит: "Имя: Иван, Возраст: 25"
// vprintf — с массивом аргументов
$data = ["Мария", 30];
vprintf("Имя: %s, Возраст: %d\n", $data);7. Безопасность строк
htmlspecialchars — защита от XSS
php
<?php
// XSS-атака: пользователь вводит HTML/JS
$userInput = '<script>alert("XSS!")</script>';
// ❌ Опасно!
echo $userInput; // Выполнится скрипт!
// ✅ Безопасно
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// <script>alert("XSS!")</script>
// Флаги:
// ENT_QUOTES — преобразует и " и '
// ENT_HTML5 — для HTML5
// ENT_SUBSTITUTE — заменяет невалидные символы
// Создай хелпер
function e(string $str): string {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
// Использование в шаблонах
echo '<input value="' . e($userInput) . '">';htmlentities — все HTML-сущности
php
<?php
$str = "Привет <World> & \"Friends\"";
echo htmlentities($str, ENT_QUOTES, 'UTF-8');
// Привет <World> & "Friends"
// html_entity_decode — обратное преобразование
$encoded = "<div>Hello</div>";
echo html_entity_decode($encoded); // "<div>Hello</div>"strip_tags — удаление HTML
php
<?php
$html = "<p>Привет <strong>мир</strong>!</p><script>alert('XSS')</script>";
// Удалить все теги
echo strip_tags($html); // "Привет мир!"
// Разрешить некоторые теги
echo strip_tags($html, '<p><strong>'); // "<p>Привет <strong>мир</strong>!</p>"
// PHP 7.4+ — массив разрешённых тегов
echo strip_tags($html, ['p', 'strong']);addslashes / stripslashes
php
<?php
// Экранирование кавычек (для строк, НЕ для SQL!)
$str = "It's a \"test\"";
echo addslashes($str); // "It\'s a \"test\""
// Обратное
echo stripslashes("It\'s a \"test\""); // "It's a "test""
// ⚠️ Для SQL используй подготовленные выражения (PDO), не addslashes!ЧАСТЬ 2: РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ
8. Введение в регулярные выражения
Что это?
Регулярные выражения (regex) — это язык для описания паттернов в тексте.
php
<?php
// Пример: проверка email
$email = "test@example.com";
$pattern = '/^[\w\.-]+@[\w\.-]+\.\w{2,}$/';
if (preg_match($pattern, $email)) {
echo "Валидный email";
}Синтаксис в PHP
php
<?php
// Регулярка заключается в разделители (обычно /)
$pattern = '/паттерн/модификаторы';
// Другие разделители (если в паттерне много /)
$pattern = '#https?://[\w./]+#';
$pattern = '~\d{4}-\d{2}-\d{2}~';Основные функции
php
<?php
// preg_match — найти первое совпадение
// preg_match_all — найти все совпадения
// preg_replace — замена по паттерну
// preg_split — разбиение по паттерну
// preg_grep — фильтрация массива9. Метасимволы и классы
Базовые метасимволы
┌──────────┬────────────────────────────────────────────────────┐
│ Символ │ Значение │
├──────────┼────────────────────────────────────────────────────┤
│ . │ Любой символ (кроме \n) │
│ ^ │ Начало строки │
│ $ │ Конец строки │
│ | │ ИЛИ (альтернатива) │
│ \ │ Экранирование спецсимвола │
│ () │ Группировка │
│ [] │ Класс символов │
└──────────┴────────────────────────────────────────────────────┘Классы символов
php
<?php
// [abc] — один из: a, b или c
// [a-z] — любая буква от a до z
// [A-Za-z]— любая буква
// [0-9] — любая цифра
// [^abc] — НЕ a, b или c (отрицание)
// [a-z0-9]— буква или цифра
preg_match('/[aeiou]/', "Hello"); // Найдёт "e"
preg_match('/[0-9]/', "Test123"); // Найдёт "1"
preg_match('/[^0-9]/', "123abc"); // Найдёт "a"Предопределённые классы
┌──────────┬──────────────────────────────────────┬─────────────┐
│ Символ │ Значение │ Эквивалент │
├──────────┼──────────────────────────────────────┼─────────────┤
│ \d │ Цифра │ [0-9] │
│ \D │ НЕ цифра │ [^0-9] │
│ \w │ "Словесный" символ │ [a-zA-Z0-9_]│
│ \W │ НЕ словесный символ │ [^a-zA-Z0-9_]│
│ \s │ Пробельный символ │ [\t\n\r\f ]│
│ \S │ НЕ пробельный символ │ [^\t\n\r\f ]│
│ \b │ Граница слова │ │
│ \B │ НЕ граница слова │ │
└──────────┴──────────────────────────────────────┴─────────────┘php
<?php
preg_match('/\d+/', "Price: 100"); // Найдёт "100"
preg_match('/\w+/', "Hello World"); // Найдёт "Hello"
preg_match('/\s+/', "Hello World"); // Найдёт " "
preg_match('/\bword\b/', "password"); // НЕ найдёт (word внутри слова)
preg_match('/\bword\b/', "the word"); // Найдёт "word"10. Квантификаторы
Базовые квантификаторы
┌──────────┬────────────────────────────────────────────────────┐
│ Символ │ Значение │
├──────────┼────────────────────────────────────────────────────┤
│ * │ 0 или более раз │
│ + │ 1 или более раз │
│ ? │ 0 или 1 раз (опционально) │
│ {n} │ Ровно n раз │
│ {n,} │ n или более раз │
│ {n,m} │ От n до m раз │
└──────────┴────────────────────────────────────────────────────┘php
<?php
// Примеры
preg_match('/a*/', "aaa"); // "aaa" (0+)
preg_match('/a+/', "aaa"); // "aaa" (1+)
preg_match('/a?/', "abc"); // "a" (0 или 1)
preg_match('/a{3}/', "aaa"); // "aaa" (ровно 3)
preg_match('/a{2,4}/', "aaaaa"); // "aaaa" (от 2 до 4)
// Практические примеры
preg_match('/\d{4}/', "Year: 2025"); // "2025" — 4 цифры
preg_match('/\d{2,4}/', "123"); // "123" — от 2 до 4 цифр
preg_match('/https?/', "http://example.com"); // "http" — s опционален
preg_match('/colou?r/', "color"); // "color" — u опционаленЖадность квантификаторов
php
<?php
$str = "<p>Hello</p><p>World</p>";
// Жадный (по умолчанию) — захватывает максимум
preg_match('/<p>.*<\/p>/', $str, $matches);
echo $matches[0]; // "<p>Hello</p><p>World</p>" — всё!
// Ленивый (добавь ?) — захватывает минимум
preg_match('/<p>.*?<\/p>/', $str, $matches);
echo $matches[0]; // "<p>Hello</p>" — только первый
// Ленивые версии квантификаторов
// *? — 0+ (ленивый)
// +? — 1+ (ленивый)
// ?? — 0-1 (ленивый)
// {n,m}? — n-m (ленивый)11. Группировка и захват
Группы захвата
php
<?php
$str = "2025-01-27";
// Группы () захватывают совпадения
preg_match('/(\d{4})-(\d{2})-(\d{2})/', $str, $matches);
print_r($matches);
/*
[
0 => "2025-01-27", // Полное совпадение
1 => "2025", // Группа 1
2 => "01", // Группа 2
3 => "27" // Группа 3
]
*/
// Деструктуризация
[, $year, $month, $day] = $matches;
echo "$day.$month.$year"; // "27.01.2025"Именованные группы
php
<?php
$str = "2025-01-27";
// Синтаксис: (?P<name>pattern) или (?<name>pattern)
$pattern = '/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/';
preg_match($pattern, $str, $matches);
echo $matches['year']; // "2025"
echo $matches['month']; // "01"
echo $matches['day']; // "27"Незахватывающие группы
php
<?php
// (?:pattern) — группировка без захвата
$str = "http://example.com";
// С захватом — протокол попадёт в $matches
preg_match('/(https?)://(.+)/', $str, $matches);
// [0 => "http://example.com", 1 => "http", 2 => "example.com"]
// Без захвата протокола
preg_match('/(?:https?)://(.+)/', $str, $matches);
// [0 => "http://example.com", 1 => "example.com"]Обратные ссылки
php
<?php
// \1, \2 — ссылки на захваченные группы
$str = "hello hello world world";
// Найти повторяющиеся слова
preg_match('/\b(\w+)\s+\1\b/', $str, $matches);
echo $matches[0]; // "hello hello"
echo $matches[1]; // "hello"
// Замена с группами
$str = "Иванов Иван";
$result = preg_replace('/(\w+)\s+(\w+)/', '$2 $1', $str);
echo $result; // "Иван Иванов"12. Модификаторы
Основные модификаторы
┌──────────┬────────────────────────────────────────────────────┐
│ Символ │ Значение │
├──────────┼────────────────────────────────────────────────────┤
│ i │ Регистронезависимый поиск │
│ m │ Многострочный режим (^ и $ для каждой строки) │
│ s │ Точка . включает \n │
│ u │ UTF-8 режим (ОБЯЗАТЕЛЕН для кириллицы!) │
│ x │ Расширенный режим (игнорирует пробелы, комментарии)│
└──────────┴────────────────────────────────────────────────────┘php
<?php
// i — без учёта регистра
preg_match('/hello/i', "HELLO World"); // Найдёт
// m — многострочный режим
$str = "Line1\nLine2\nLine3";
preg_match_all('/^\w+/m', $str, $matches);
// ["Line1", "Line2", "Line3"]
// s — точка включает перенос
$str = "Hello\nWorld";
preg_match('/Hello.World/s', $str); // Найдёт
// u — UTF-8 (ОБЯЗАТЕЛЬНО для кириллицы!)
preg_match('/\w+/u', "Привет"); // Работает корректно
// x — расширенный режим (с комментариями)
$pattern = '/
^ # Начало строки
[\w\.\-]+ # Имя пользователя
@ # Символ @
[\w\.\-]+ # Домен
\. # Точка
\w{2,} # Зона (минимум 2 символа)
$ # Конец строки
/x';13. preg_match и preg_match_all
preg_match — первое совпадение
php
<?php
$str = "My phone: +7 (999) 123-45-67";
// Простая проверка
if (preg_match('/\d{3}/', $str)) {
echo "Найдены цифры";
}
// С захватом результата
if (preg_match('/\+7\s*\((\d{3})\)\s*(\d{3})-(\d{2})-(\d{2})/', $str, $matches)) {
print_r($matches);
/*
[
0 => "+7 (999) 123-45-67",
1 => "999",
2 => "123",
3 => "45",
4 => "67"
]
*/
}
// С флагом PREG_OFFSET_CAPTURE — позиции совпадений
preg_match('/\d+/', "Test123", $matches, PREG_OFFSET_CAPTURE);
// [0 => ["123", 4]] — значение и позицияpreg_match_all — все совпадения
php
<?php
$str = "Email: test@example.com, another@test.org";
// Найти все email
$pattern = '/[\w\.-]+@[\w\.-]+\.\w+/';
preg_match_all($pattern, $str, $matches);
print_r($matches[0]);
// ["test@example.com", "another@test.org"]
// С группами
$str = "Цены: 100 руб., 250 руб., 1500 руб.";
preg_match_all('/(\d+)\s*руб/', $str, $matches);
print_r($matches[0]); // ["100 руб", "250 руб", "1500 руб"]
print_r($matches[1]); // ["100", "250", "1500"]
// PREG_SET_ORDER — группировка по совпадениям
preg_match_all('/(\d+)\s*руб/', $str, $matches, PREG_SET_ORDER);
/*
[
[0 => "100 руб", 1 => "100"],
[0 => "250 руб", 1 => "250"],
[0 => "1500 руб", 1 => "1500"]
]
*/14. preg_replace — замена
Простая замена
php
<?php
$str = "Hello World";
// Замена по паттерну
echo preg_replace('/World/', 'PHP', $str); // "Hello PHP"
// С модификаторами
echo preg_replace('/world/i', 'PHP', $str); // "Hello PHP"
// Массив паттернов
$patterns = ['/\bHello\b/', '/\bWorld\b/'];
$replacements = ['Привет', 'Мир'];
echo preg_replace($patterns, $replacements, $str); // "Привет Мир"Замена с группами
php
<?php
// Использование $1, $2... для групп
$str = "Иванов Иван Сергеевич";
$result = preg_replace('/(\w+)\s+(\w+)\s+(\w+)/u', '$2 $3 $1', $str);
echo $result; // "Иван Сергеевич Иванов"
// Форматирование телефона
$phone = "79991234567";
$formatted = preg_replace('/(\d)(\d{3})(\d{3})(\d{2})(\d{2})/', '+$1 ($2) $3-$4-$5', $phone);
echo $formatted; // "+7 (999) 123-45-67"
// Удаление HTML-тегов (альтернатива strip_tags)
$html = "<p>Hello <b>World</b></p>";
echo preg_replace('/<[^>]+>/', '', $html); // "Hello World"preg_replace_callback — замена функцией
php
<?php
$str = "Цены: 100, 250, 1500";
// Увеличить все числа на 10%
$result = preg_replace_callback(
'/\d+/',
fn($matches) => $matches[0] * 1.1,
$str
);
echo $result; // "Цены: 110, 275, 1650"
// Форматирование дат
$str = "Даты: 2025-01-27, 2024-12-31";
$result = preg_replace_callback(
'/(\d{4})-(\d{2})-(\d{2})/',
fn($m) => "{$m[3]}.{$m[2]}.{$m[1]}",
$str
);
echo $result; // "Даты: 27.01.2025, 31.12.2024"
// Подсветка кода
$code = "Переменные: \$name, \$age, \$city";
$highlighted = preg_replace_callback(
'/\$\w+/',
fn($m) => "<span class='variable'>{$m[0]}</span>",
$code
);15. preg_split и preg_grep
preg_split — разбиение по паттерну
php
<?php
$str = "Hello, World; PHP";
// По разделителям (один или несколько)
$words = preg_split('/[\s,;]+/', $str);
// ["Hello", "World", "PHP"]
// По CamelCase
$str = "camelCaseString";
$parts = preg_split('/(?=[A-Z])/', $str);
// ["camel", "Case", "String"]
// С захватом разделителей
$str = "Hello, World; PHP";
$parts = preg_split('/([\s,;]+)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
// ["Hello", ", ", "World", "; ", "PHP"]
// Лимит
$parts = preg_split('/\s+/', "a b c d e", 3);
// ["a", "b", "c d e"]preg_grep — фильтрация массива
php
<?php
$files = ["image.jpg", "document.pdf", "photo.png", "text.txt", "icon.gif"];
// Только изображения
$images = preg_grep('/\.(jpg|png|gif)$/', $files);
// ["image.jpg", "photo.png", "icon.gif"]
// Инверсия — всё кроме изображений
$notImages = preg_grep('/\.(jpg|png|gif)$/', $files, PREG_GREP_INVERT);
// ["document.pdf", "text.txt"]
// Фильтрация email
$strings = ["test@example.com", "not-email", "another@test.org", "123"];
$emails = preg_grep('/^[\w\.-]+@[\w\.-]+\.\w+$/', $strings);
// ["test@example.com", "another@test.org"]16. Практические примеры регулярок
Валидация
php
<?php
class Validator {
// Email
public static function email(string $email): bool {
return (bool) preg_match(
'/^[\w\.\-]+@[\w\.\-]+\.[a-z]{2,}$/i',
$email
);
}
// Телефон (российский)
public static function phone(string $phone): bool {
$cleaned = preg_replace('/\D/', '', $phone);
return (bool) preg_match('/^[78]?\d{10}$/', $cleaned);
}
// URL
public static function url(string $url): bool {
return (bool) preg_match(
'#^https?://[\w\-\.]+(:\d+)?(/[\w\-\./]*)?(\?[\w=&\-]*)?$#i',
$url
);
}
// Дата (YYYY-MM-DD)
public static function date(string $date): bool {
if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $date, $m)) {
return false;
}
return checkdate((int)$m[2], (int)$m[3], (int)$m[1]);
}
// Пароль (минимум 8 символов, буквы и цифры)
public static function password(string $password): bool {
return strlen($password) >= 8
&& preg_match('/[a-z]/i', $password)
&& preg_match('/\d/', $password);
}
// Только буквы (включая кириллицу)
public static function alpha(string $str): bool {
return (bool) preg_match('/^[\p{L}]+$/u', $str);
}
// Буквы и цифры
public static function alphaNum(string $str): bool {
return (bool) preg_match('/^[\p{L}\d]+$/u', $str);
}
}
// Использование
var_dump(Validator::email("test@example.com")); // true
var_dump(Validator::phone("+7 (999) 123-45-67")); // true
var_dump(Validator::url("https://example.com/path?query=1")); // true
var_dump(Validator::date("2025-02-30")); // false (30 февраля не существует)Парсинг данных
php
<?php
// Извлечение всех URL из текста
function extractUrls(string $text): array {
preg_match_all(
'#https?://[\w\-\.]+(?:/[\w\-\./]*)?(?:\?[\w=&\-]*)?#i',
$text,
$matches
);
return $matches[0];
}
$text = "Visit https://example.com or http://test.org/page?id=1";
print_r(extractUrls($text));
// ["https://example.com", "http://test.org/page?id=1"]
// Извлечение хештегов
function extractHashtags(string $text): array {
preg_match_all('/#([\w\p{L}]+)/u', $text, $matches);
return $matches[1];
}
$text = "Привет #мир! Это #PHP и #программирование";
print_r(extractHashtags($text));
// ["мир", "PHP", "программирование"]
// Парсинг CSV с кавычками
function parseCSVLine(string $line): array {
preg_match_all('/"([^"]*)"|([^,]+)/', $line, $matches);
$result = [];
foreach ($matches[0] as $i => $match) {
$result[] = $matches[1][$i] !== '' ? $matches[1][$i] : $matches[2][$i];
}
return $result;
}
$line = 'Иван,"Москва, Россия",25';
print_r(parseCSVLine($line));
// ["Иван", "Москва, Россия", "25"]Трансформация текста
php
<?php
// Slug из строки
function slugify(string $str): string {
$translit = [
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n',
'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'ts', 'ч' => 'ch',
'ш' => 'sh', 'щ' => 'sch', 'ъ' => '', 'ы' => 'y', 'ь' => '',
'э' => 'e', 'ю' => 'yu', 'я' => 'ya'
];
$str = mb_strtolower($str);
$str = strtr($str, $translit);
$str = preg_replace('/[^a-z0-9]+/', '-', $str);
$str = trim($str, '-');
return $str;
}
echo slugify("Привет, Мир! Это PHP"); // "privet-mir-eto-php"
// CamelCase в snake_case
function toSnakeCase(string $str): string {
return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $str));
}
echo toSnakeCase("camelCaseString"); // "camel_case_string"
// snake_case в CamelCase
function toCamelCase(string $str): string {
return lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $str))));
}
echo toCamelCase("snake_case_string"); // "snakeCaseString"
// Выделение слов в тексте (подсветка поиска)
function highlightWords(string $text, array $words): string {
$pattern = '/\b(' . implode('|', array_map('preg_quote', $words)) . ')\b/iu';
return preg_replace($pattern, '<mark>$1</mark>', $text);
}
$text = "PHP - это язык программирования для веб-разработки";
echo highlightWords($text, ["PHP", "язык"]);
// "<mark>PHP</mark> - это <mark>язык</mark> программирования..."17. Упражнения
Упражнение 1: Строковые функции (15 минут)
php
<?php
$str = " Hello, World! This is PHP. ";
// 1. Убери пробелы по краям
// 2. Преобразуй в верхний регистр
// 3. Замени "World" на "Universe"
// 4. Подсчитай количество слов
// 5. Разбей на массив слов
// 6. Получи первые 5 символовУпражнение 2: Форматирование данных (20 минут)
php
<?php
// 1. Напиши функцию форматирования телефона
// formatPhone("79991234567") → "+7 (999) 123-45-67"
// 2. Напиши функцию форматирования размера файла
// formatFileSize(1536) → "1.5 KB"
// formatFileSize(1048576) → "1 MB"
// 3. Напиши функцию обрезки текста с многоточием
// truncate("Hello World PHP", 10) → "Hello W..."
// truncate("Hi", 10) → "Hi"Упражнение 3: Регулярные выражения (25 минут)
php
<?php
// 1. Напиши regex для проверки российского паспорта (XX XX XXXXXX)
// 2. Извлеки все числа из строки "Цена: 100 руб., скидка 15%, итого: 85 руб."
// 3. Найди все слова, начинающиеся с заглавной буквы
// 4. Замени все множественные пробелы на один
// 5. Проверь, является ли строка валидным IPv4 адресомУпражнение 4: Практические задачи (30 минут)
php
<?php
$html = '<a href="https://example.com">Example</a> и <a href="https://test.org">Test</a>';
// 1. Извлеки все ссылки (URL и текст) из HTML
// Результат: [["url" => "https://example.com", "text" => "Example"], ...]
// 2. Напиши функцию очистки HTML от опасных тегов и атрибутов
// Оставить только: p, a (только href), strong, em
// 3. Напиши функцию автоматического создания ссылок из URL в тексте
// "Visit https://example.com for more" → "Visit <a href="...">https://example.com</a> for more"18. Вопросы для самопроверки
Чем отличаются одинарные и двойные кавычки?
Почему для кириллицы нужно использовать
mb_*функции?Как безопасно вывести пользовательский ввод в HTML?
Чем отличается
strpos()отstr_contains()?Что делает модификатор
uв регулярках?В чём разница между жадными и ленивыми квантификаторами?
Как получить все совпадения, а не только первое?
Что такое группы захвата и обратные ссылки?
19. Частые ошибки
Ошибка 1: Проверка strpos через ==
php
<?php
$str = "Hello World";
// ❌ Неправильно (0 == false)
if (strpos($str, "Hello")) {
echo "Найдено"; // Не выполнится!
}
// ✅ Правильно
if (strpos($str, "Hello") !== false) {
echo "Найдено";
}
// ✅ PHP 8+
if (str_contains($str, "Hello")) {
echo "Найдено";
}Ошибка 2: strlen для UTF-8
php
<?php
$str = "Привет";
// ❌ Неправильно — считает байты
echo strlen($str); // 12 (не 6!)
// ✅ Правильно
echo mb_strlen($str); // 6Ошибка 3: Забыт модификатор u
php
<?php
$str = "Привет мир";
// ❌ Неправильно — \w не работает с кириллицей
preg_match('/\w+/', $str, $matches);
// $matches[0] может быть пустым или мусором
// ✅ Правильно
preg_match('/\w+/u', $str, $matches);
// $matches[0] = "Привет"
// Или используй \p{L} для букв любого языка
preg_match('/[\p{L}]+/u', $str, $matches);Ошибка 4: XSS-уязвимость
php
<?php
$userInput = $_GET['name'];
// ❌ ОПАСНО!
echo "Привет, $userInput!";
// ✅ Безопасно
echo "Привет, " . htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8') . "!";Ошибка 5: Неэкранированные спецсимволы в regex
php
<?php
$search = "C++";
// ❌ Ошибка — + это квантификатор
preg_match("/$search/", "Learn C++");
// ✅ Экранировать пользовательский ввод
preg_match('/' . preg_quote($search, '/') . '/', "Learn C++");20. Шпаргалка по регулярным выражениям
┌─────────────────────────────────────────────────────────────────┐
│ ШПАРГАЛКА REGEX │
├─────────────────────────────────────────────────────────────────┤
│ МЕТАСИМВОЛЫ │
│ . Любой символ ^ Начало строки │
│ $ Конец строки | ИЛИ │
│ \ Экранирование () Группа захвата │
│ [] Класс символов [^] Отрицание класса │
├─────────────────────────────────────────────────────────────────┤
│ КЛАССЫ СИМВОЛОВ │
│ \d Цифра [0-9] \D Не цифра │
│ \w Слово [a-zA-Z0-9_] \W Не слово │
│ \s Пробел \S Не пробел │
│ \b Граница слова \p{L} Буква (Unicode) │
├─────────────────────────────────────────────────────────────────┤
│ КВАНТИФИКАТОРЫ │
│ * 0 или более + 1 или более │
│ ? 0 или 1 {n} Ровно n │
│ {n,} n или более {n,m} От n до m │
│ *? +? Ленивые версии │
├─────────────────────────────────────────────────────────────────┤
│ МОДИФИКАТОРЫ │
│ i Регистронезависимый m Многострочный │
│ s Точка включает \n u UTF-8 (ОБЯЗАТЕЛЕН!) │
│ x Расширенный режим │
├─────────────────────────────────────────────────────────────────┤
│ ФУНКЦИИ PHP │
│ preg_match() Первое совпадение │
│ preg_match_all() Все совпадения │
│ preg_replace() Замена │
│ preg_replace_callback() Замена функцией │
│ preg_split() Разбиение │
│ preg_grep() Фильтрация массива │
│ preg_quote() Экранирование спецсимволов │
└─────────────────────────────────────────────────────────────────┘Резюме главы
┌────────────────────────────────────────────────────────────────┐
│ ЗАПОМНИ ГЛАВНОЕ │
├────────────────────────────────────────────────────────────────┤
│ │
│ СТРОКИ │
│ • 'одинарные' — без интерполяции │
│ • "двойные" — с интерполяцией $var и спецсимволами │
│ • Для UTF-8 используй mb_* функции │
│ • htmlspecialchars() — защита от XSS │
│ │
│ ОСНОВНЫЕ ФУНКЦИИ │
│ • strlen/mb_strlen — длина │
│ • strpos/mb_strpos — поиск позиции │
│ • str_contains/starts_with/ends_with — проверки (PHP 8+) │
│ • str_replace — замена подстроки │
│ • explode/implode — разбиение/объединение │
│ • sprintf — форматирование │
│ • trim — обрезка пробелов │
│ │
│ РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ │
│ • /паттерн/модификаторы — синтаксис │
│ • u — ОБЯЗАТЕЛЕН для кириллицы! │
│ • preg_match — первое совпадение │
│ • preg_match_all — все совпадения │
│ • preg_replace — замена │
│ • preg_replace_callback — замена функцией │
│ • preg_quote — экранирование пользовательского ввода │
│ │
│ БЕЗОПАСНОСТЬ │
│ • Всегда экранируй вывод: htmlspecialchars() │
│ • Экранируй regex: preg_quote() │
│ • Валидируй ввод перед использованием │
│ │
└────────────────────────────────────────────────────────────────┘Следующая глава: Глава 1.6: Работа с файлами — чтение, запись, загрузка файлов от пользователя, безопасность