Строки в PHP: рабочие приёмы и типичные ошибки
Строковые функции PHP кажутся простыми, пока не столкнёшься с кириллицей, которая ломает substr, или mb_-функциями без явной кодировки. Разбираем приёмы, которые не подведут на UTF-8.

Строки в PHP — первое, с чем работает любой скрипт: разобрать запрос, склеить SQL, вывести имя пользователя. Проблема всплывает, когда в данных появляется кириллица. Классический strlen("Привет") вернёт не 6, а 12, а substr обрежет слово посреди байта и выдаст «кракозябру». Здесь собраны приёмы, которые работают на UTF-8 и не разваливаются на многобайтных символах. Актуально для PHP 8.x, но большинство функций живёт в языке с версии 5.
Байты против символов: главная развилка
В PHP строка — это последовательность байтов, а не символов. Для ASCII байт равен символу, и всё работает интуитивно. Для UTF-8 один символ кириллицы занимает 2 байта, эмодзи — до 4. Отсюда деление на два семейства функций.
- Обычные —
strlen,substr,strpos,strtoupper. Считают и режут по байтам. Быстрые, но на кириллице врут. - Мультибайтные —
mb_strlen,mb_substr,mb_strpos,mb_strtoupper. Учитывают кодировку. Требуют расширенияmbstring.
Проверьте, что mbstring включено:
<?php
// Вернёт true, если расширение доступно
var_dump(extension_loaded('mbstring'));
Если вернулось false, mb_-функции недоступны. На большинстве хостингов расширение включено по умолчанию, но на минимальной сборке PHP его может не быть.
Всегда указывайте кодировку явно
Частая ошибка — полагаться на глобальную настройку mbstring.internal_encoding. Она может отличаться между окружениями, и код, который работал на локальной машине, посчитает длину неверно на проде. Передавайте кодировку четвёртым (или соответствующим) аргументом.
<?php
$name = "Привет";
// strlen считает байты: 12
echo strlen($name);
// mb_strlen с явной кодировкой: 6 символов
echo mb_strlen($name, 'UTF-8');
// Обрезка первых трёх символов
echo mb_substr($name, 0, 3, 'UTF-8'); // При
Склейка, разбор и замена
Здесь многобайтность влияет реже — большинство операций либо работают по разделителю, либо не зависят от длины символа.
Разбить строку на части
<?php
$csvLine = "иванов;менеджер;отдел продаж";
// explode делит по разделителю, кодировка не важна
$parts = explode(';', $csvLine);
// [0 => 'иванов', 1 => 'менеджер', 2 => 'отдел продаж']
// Ограничить число частей — четвёртый пример разбивает на две
$firstTwo = explode(';', $csvLine, 2);
// [0 => 'иванов', 1 => 'менеджер;отдел продаж']
Собрать обратно
<?php
$parts = ['москва', 'тверская', '12'];
// implode склеивает через разделитель
echo implode(', ', $parts); // москва, тверская, 12
Замена подстроки
str_replace работает по байтам, но для точных подстрок (не по регулярным выражениям) это безопасно — она ищет полное совпадение последовательности байтов, а не режет посередине символа.
<?php
$text = "Заказ №100 отменён";
// Простая замена подстроки
echo str_replace('отменён', 'выполнен', $text);
// Заказ №100 выполнен
// Несколько замен за один вызов
$dirty = "тел: +7 (999) 000-00-00";
$clean = str_replace([' ', '(', ')', '-'], '', $dirty);
// +79990000000
Правило по умолчанию: если в строке может оказаться кириллица или эмодзи и вам важна длина или позиция символа — берите mb_-функцию и указывайте UTF-8. Если работаете с разделителями или полными подстроками — обычные функции безопасны и быстрее.
Проверка и очистка ввода
Строки из формы и запроса нельзя доверять. До проверки формата уберите лишние пробелы по краям.
<?php
$raw = " user@example.com ";
// Убрать пробелы, табы и переводы строк по краям
$email = trim($raw);
// Проверить пустоту после обрезки
if ($email === '') {
echo 'Поле пустое';
}
// Найти подстроку. str_contains — с PHP 8.0
if (str_contains($email, '@')) {
echo 'Похоже на адрес';
}
Функции str_contains, str_starts_with и str_ends_with появились в PHP 8.0. До этого приходилось писать strpos($haystack, $needle) !== false — с обязательной проверкой на !== false, потому что совпадение в нулевой позиции возвращает 0, а его if примет за «не найдено».
Регистр и первая буква
<?php
$city = "санкт-петербург";
// Верхний регистр с учётом кодировки
echo mb_strtoupper($city, 'UTF-8'); // САНКТ-ПЕТЕРБУРГ
// ucfirst НЕ работает с кириллицей корректно —
// она байтовая. Для UTF-8 делаем вручную:
$first = mb_strtoupper(mb_substr($city, 0, 1, 'UTF-8'), 'UTF-8');
$rest = mb_substr($city, 1, null, 'UTF-8');
echo $first . $rest; // Санкт-петербург
Форматирование чисел и подстановка значений
Для сборки строк с числами удобнее sprintf, чем конкатенация через точку — читаемее и позволяет управлять форматом.
<?php
$price = 1234.5;
$qty = 3;
// %s — строка, %d — целое, %.2f — дробное с двумя знаками
$line = sprintf('Товар x%d = %.2f руб.', $qty, $price * $qty);
echo $line; // Товар x3 = 3703.50 руб.
// number_format для денег: разделители разрядов
echo number_format(3703.5, 2, ',', ' '); // 3 703,50
Таблица: что взять под задачу
| Задача | Функция | Осторожно с |
|---|---|---|
| Длина строки в символах | mb_strlen | strlen считает байты |
| Обрезать N символов | mb_substr | substr режет по байтам |
| Найти подстроку (PHP 8+) | str_contains | strpos требует !== false |
| Разбить по разделителю | explode | — |
| Замена точной подстроки | str_replace | — |
| Верхний/нижний регистр | mb_strtoupper / mb_strtolower | strtoupper и кириллица |
| Убрать пробелы по краям | trim | внутри строки не трогает |
| Формат денег | number_format | — |
Типичные ошибки
- strlen на кириллице. Ограничение поля «до 20 символов» через
strlenобрежет русский текст вдвое короче ожидаемого. Беритеmb_strlen. - strpos без строгого сравнения.
if (strpos($str, 'а'))вернётfalse-подобный ноль, если подстрока в самом начале. Пишите!== falseили переходите наstr_contains. - substr для «многоточия». Обрезка анонса статьи через
substrразорвёт символ и выдаст?или мусор. Используйтеmb_substr. - Забытая кодировка в mb_-функции. Без явного
'UTF-8'результат зависит от настройки сервера. Указывайте всегда. - str_replace вместо экранирования для SQL. Чистка кавычек вручную — не защита от инъекций. Используйте подготовленные выражения PDO, а строковые функции оставьте для отображения.
Что настроить дальше
Проверьте в вашем окружении значение mbstring.internal_encoding через ini_get('mbstring.internal_encoding') — оно должно быть UTF-8, но не полагайтесь на него в коде. Для сложного разбора текста (шаблоны, извлечение по маске) следующий шаг — preg_match и preg_replace с модификатором u, который включает поддержку Unicode; без него регулярка так же порежет кириллицу по байтам. Полный список строковых функций и их поведение с многобайтными строками смотрите в разделе Strings официальной документации PHP.
Субтитры и таймкоды: Расшифровка речи и субтитры по правилам читаемости: 42 знака в строке, две строки, минимум секунда на реплику.
Спросить за 40 ₽Источники: PHP Manual — Strings, PHP Manual — Multibyte String Functions
Частые вопросы
А если mbstring не установлено на хостинге?
У меня старый PHP 7.x, str_contains не работает
Почему ucfirst портит русскую первую букву?
Строка приходит в другой кодировке, не UTF-8
Можно ли чистить кавычки str_replace, чтобы защититься от SQL-инъекций?
number_format округляет неправильно с деньгами
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.