Строка из поля формы редко приходит чистой. По краям — пробелы и переводы строк, внутри — неразрывные пробелы из скопированного текста, иногда случайные кавычки, символы форматирования и невидимые управляющие коды. Прежде чем сохранять такие данные, сравнивать их или искать по ним, строку приводят к нормальному виду. В PHP для этого есть набор простых инструментов, и ниже они разобраны от простого к сложному. Отдельный раздел в конце — про то, чего очистка не делает: защита от инъекций устроена иначе, и путать эти две задачи опасно.
Пробелы по краям: trim()
$name = trim( $_POST['name'] ?? '' );
Функция trim() убирает с обоих концов строки пробелы, табуляции, переводы строк и нулевой байт. Есть парные ltrim() и rtrim() для одной стороны и второй аргумент со своим списком символов — например, trim( $s, " \t." ) уберёт ещё и точки. Оператор ?? '' защищает от предупреждения, если поля в запросе не оказалось.
Неразрывный пробел trim() не видит
Текст, скопированный из Word или с веб-страницы, часто содержит неразрывный пробел (U+00A0) — внешне такой же, но trim() его не трогает. Универсальный вариант через регулярное выражение с флагом u для UTF-8:
$s = preg_replace( '/^[\s\x{00A0}]+|[\s\x{00A0}]+$/u', '', $s );
Той же конструкцией удобно схлопывать повторяющиеся пробелы внутри строки: preg_replace( '/[\s\x{00A0}]+/u', ' ', $s ) заменит любую последовательность пробельных символов одним пробелом.
Удалить конкретные символы: str_replace() и strtr()
Когда список нежелательных символов известен, проще всего заменить их на пустую строку. str_replace() принимает массивы, поэтому все символы удаляются одним вызовом:
$s = str_replace( array( '^', '*', '"', "'" ), '', $s );
Функция strtr() с массивом «что → на что» делает то же самое, но с одним отличием: она проходит строку один раз и не применяет замены к результату предыдущих замен. Это важно, когда замены могут «зацепить» друг друга:
$s = strtr( $s, array( '«' => '"', '»' => '"', '—' => '-' ) );
Оба варианта хороши для коротких списков. Если нужно описать не «что удалить», а «что оставить», удобнее регулярные выражения.
Оставить только разрешённое: preg_replace()
Белый список надёжнее чёрного: вы перечисляете допустимые символы, а всё остальное удаляется, включая то, о чём вы не подумали.
// Только цифры — для телефона или кода
$digits = preg_replace( '/\D+/', '', $phone );
// Буквы любого алфавита, цифры, пробел и дефис — для имени или названия
$title = preg_replace( '/[^\p{L}\p{N}\s\-]/u', '', $title );
// Убрать невидимые управляющие символы, оставив перевод строки и табуляцию
$text = preg_replace( '/[^\P{C}\n\t]/u', '', $text );
Классы \p{L} и \p{N} означают «буква» и «цифра» в любом языке, \p{C} — управляющие и невидимые символы; всё это работает только с флагом u. Без него регулярное выражение считает кириллицу набором байтов и может испортить строку.
Числа и email: filter_var() и приведение типа
Если ожидается число, вырезать лишнее из строки не нужно — достаточно привести тип или проверить значение:
$qty = (int) ( $_POST['qty'] ?? 0 ); // «12 шт.» → 12, «abc» → 0
$id = filter_var( $_GET['id'] ?? '', FILTER_VALIDATE_INT, array(
'options' => array( 'min_range' => 1 ),
) ); // число ≥ 1 или false
$email = filter_var( trim( $_POST['email'] ?? '' ), FILTER_VALIDATE_EMAIL ); // адрес или false
Фильтры FILTER_VALIDATE_* проверяют значение и возвращают false, если оно не подходит, — это надёжнее, чем очистка, потому что подделанный ввод отбрасывается целиком. Из «очищающих» фильтров по-прежнему полезны FILTER_SANITIZE_NUMBER_INT и FILTER_SANITIZE_EMAIL; фильтр для строк общего назначения FILTER_SANITIZE_STRING объявлен устаревшим с PHP 8.1, и вместо него используют htmlspecialchars() при выводе либо strip_tags(), если HTML в данных недопустим.
Готовая функция нормализации текста
Для полей вроде имени, названия или комментария обычно нужно одно и то же: убрать невидимые символы, схлопнуть пробелы, обрезать края и при необходимости ограничить длину. Соберём это в одну функцию:
function normalize_text( string $s, int $max_length = 0 ): string {
// 1. Убираем управляющие и невидимые символы, кроме перевода строки и табуляции
$s = preg_replace( '/[^\P{C}\n\t]/u', '', $s );
// 2. Неразрывные и повторяющиеся пробелы → один обычный пробел
$s = preg_replace( '/[ \t\x{00A0}]+/u', ' ', $s );
// 3. Пробелы и переводы строк по краям
$s = preg_replace( '/^[\s\x{00A0}]+|[\s\x{00A0}]+$/u', '', $s );
// 4. Ограничение длины с учётом многобайтовых символов
if ( $max_length > 0 ) {
$s = mb_substr( $s, 0, $max_length );
}
return $s;
}
$name = normalize_text( $_POST['name'] ?? '', 100 );
$comment = normalize_text( $_POST['comment'] ?? '', 2000 );
Обратите внимание, что функция не трогает кавычки, апострофы и знаки препинания: фамилия O'Brien или название в кавычках — нормальные данные, и терять их незачем. Безопасность при выводе и записи обеспечивается на следующем шаге.
Чего очистка не делает: защита от инъекций
Удаление кавычек и «опасных» символов не защищает ни от SQL-инъекций, ни от XSS. Атакующему хватает конструкций без кавычек, а честные пользователи при этом теряют апострофы в фамилиях. Защита строится не на очистке входа, а на правильном обращении с данными в каждом конкретном месте:
- База данных — подготовленные запросы:
$pdo->prepare( 'SELECT … WHERE id = ?' )с передачей значений отдельно от SQL. В WordPress —$wpdb->prepare(). - HTML — экранирование при выводе:
htmlspecialchars( $s, ENT_QUOTES, 'UTF-8' ). В WordPress —esc_html(),esc_attr(). - URL —
rawurlencode()для параметров, в WordPressesc_url(). - Командная строка —
escapeshellarg(), а лучше вовсе не передавать пользовательский ввод в команды.
Правило простое: очистка приводит данные к ожидаемому виду, а экранирование делает их безопасными для конкретного получателя. Первое делают один раз при получении, второе — каждый раз в момент использования, потому что для базы, HTML и URL «безопасно» означает разное.
В WordPress обе задачи закрыты штатными функциями: sanitize_text_field() делает почти то же, что функция выше (убирает теги, управляющие символы и лишние пробелы), sanitize_email() и absint() обрабатывают адреса и числа, а семейство esc_* отвечает за вывод.
Коротко
Пробелы по краям убирает trim(), но неразрывный пробел требует preg_replace() с флагом u. Известные символы удаляются через str_replace() или strtr(), а надёжнее описать белый список разрешённых символов. Для чисел и email используйте приведение типа и filter_var(). Очистка нормализует данные, но не защищает от инъекций — для этого нужны подготовленные запросы и экранирование при выводе.
Частые вопросы
Скорее всего, это неразрывный пробел U+00A0 из скопированного текста — trim() его не считает пробелом. Используйте preg_replace( '/^[\s\x{00A0}]+|[\s\x{00A0}]+$/u', '', $s ).
Нет. Защита от инъекций — это подготовленные запросы (PDO::prepare, $wpdb->prepare), которые передают значения отдельно от SQL. Удаление кавычек портит честные данные и не останавливает атаку.
Фильтр устарел с PHP 8.1. Для вывода в HTML используйте htmlspecialchars() в момент вывода, для удаления тегов — strip_tags(), для общей нормализации — функцию из статьи или sanitize_text_field() в WordPress.
preg_replace( '/[^\p{L}\p{N}\s]/u', '', $s ). Классы \p{L} и \p{N} охватывают буквы и цифры любого алфавита; флаг u обязателен для UTF-8.
Источники
- 1.trim — PHP Manualhttps://www.php.net/manual/ru/function.trim.php
- 2.preg_replace — PHP Manualhttps://www.php.net/manual/ru/function.preg-replace.php
- 3.filter_var и типы фильтров — PHP Manualhttps://www.php.net/manual/ru/filter.filters.php
- 4.Prepared statements — PDO, PHP Manualhttps://www.php.net/manual/ru/pdo.prepared-statements.php



