PHP

Как очистить строку в PHP от лишних символов: пробелы, кавычки, мусор и где на самом деле защита

M
Markabus
·
Фотореалистичный кадр на деревянном столе: мелкое стальное сито на керамической миске, в миске просеянные макароны-буквы, а в сите остались камешки, обрывок бечёвки, проволочная стяжка и кусочек скорлупы, на заднем плане в расфокусе ноутбук с редактором кода — иллюстрация к статье об очистке строки в PHP от лишних символов.

Строка из поля формы редко приходит чистой. По краям — пробелы и переводы строк, внутри — неразрывные пробелы из скопированного текста, иногда случайные кавычки, символы форматирования и невидимые управляющие коды. Прежде чем сохранять такие данные, сравнивать их или искать по ним, строку приводят к нормальному виду. В 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() для параметров, в WordPress esc_url().
  • Командная строка — escapeshellarg(), а лучше вовсе не передавать пользовательский ввод в команды.

Правило простое: очистка приводит данные к ожидаемому виду, а экранирование делает их безопасными для конкретного получателя. Первое делают один раз при получении, второе — каждый раз в момент использования, потому что для базы, HTML и URL «безопасно» означает разное.

В WordPress обе задачи закрыты штатными функциями: sanitize_text_field() делает почти то же, что функция выше (убирает теги, управляющие символы и лишние пробелы), sanitize_email() и absint() обрабатывают адреса и числа, а семейство esc_* отвечает за вывод.

Коротко

Пробелы по краям убирает trim(), но неразрывный пробел требует preg_replace() с флагом u. Известные символы удаляются через str_replace() или strtr(), а надёжнее описать белый список разрешённых символов. Для чисел и email используйте приведение типа и filter_var(). Очистка нормализует данные, но не защищает от инъекций — для этого нужны подготовленные запросы и экранирование при выводе.

Частые вопросы

Q.Почему trim() не убирает пробел в конце строки?

Скорее всего, это неразрывный пробел U+00A0 из скопированного текста — trim() его не считает пробелом. Используйте preg_replace( '/^[\s\x{00A0}]+|[\s\x{00A0}]+$/u', '', $s ).

Q.Нужно ли удалять кавычки из данных формы для защиты от SQL-инъекций?

Нет. Защита от инъекций — это подготовленные запросы (PDO::prepare, $wpdb->prepare), которые передают значения отдельно от SQL. Удаление кавычек портит честные данные и не останавливает атаку.

Q.Чем заменить FILTER_SANITIZE_STRING?

Фильтр устарел с PHP 8.1. Для вывода в HTML используйте htmlspecialchars() в момент вывода, для удаления тегов — strip_tags(), для общей нормализации — функцию из статьи или sanitize_text_field() в WordPress.

Q.Как оставить в строке только буквы и цифры, включая кириллицу?

preg_replace( '/[^\p{L}\p{N}\s]/u', '', $s ). Классы \p{L} и \p{N} охватывают буквы и цифры любого алфавита; флаг u обязателен для UTF-8.

Источники

←
Предыдущая
Action Scheduler для асинхронного выполнения задач в WooCommerce

Читайте также