Александр Некрасов · Автономное руководство

PHP: полный мануал с примерами, функциями и ООП

Разделы этой страницы
  1. Фильтрация данных: полный справочник Filter
  2. Сначала задача: принять количество, не подменив ввод
  3. Карта всех функций и фильтров
  4. Результат, отказ, отсутствие, default и форма массива
  5. SAPI-снимок: от запроса к переменной
  6. Целое: диапазон, ноль, пробелы и основания
  7. Логическое значение: false не равно «ошибка»
  8. Дробь: разделитель, диапазон и точность
  9. Собственный формат: FILTER_VALIDATE_REGEXP
  10. URL: синтаксис, протокол и назначение
  11. Домен и hostname — не один уровень проверки
  12. Email: форма адреса и подтверждение получателя
  13. IP: версия адреса, частные диапазоны и global
  14. MAC: формат идентификатора оборудования
  15. RAW и флаги удаления/кодирования байтов
  16. HTML: SPECIAL_CHARS и FULL_SPECIAL_CHARS
  17. Очистка email может поменять получателя
  18. Очистка URL и URL-кодирование — разные задачи
  19. Числовой санитайзер возвращает текст и может менять число
  20. ADD_SLASHES: точный механизм и предел применимости
  21. STRING/STRIPPED: понять старый код и заменить по назначению
  22. FILTER_CALLBACK: своё правило и произвольный результат
  23. Установка, INI и версии при сопровождении
  24. Собрать ошибки заказа, затем передать проверенные значения
  25. Функции и методы этой темы
  26. Код и руководство с собой
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 82 · Практика

Фильтрация данных: полный справочник Filter

Все семь функций, валидаторы, санитайзеры, флаги, ошибки, HTTP-вход и практика без потери исходных данных.

Сначала задача: принять количество, не подменив ввод

Наглядная модель

Из текста поля в проверенное количество

У формы qty приходит строкой. Проверка отвечает, можно ли использовать её как целое число в заданном диапазоне.

  1. qty = строка «2»

    Сначала исключаем массив вместо одного поля.

  2. FILTER_VALIDATE_INT

    Правило: целое число от 1 до 10.

  3. Результат: int 2

    Сохраняем проверенное число отдельно от исходного текста.

  4. «12» → false

    Не прошло диапазон: возвращаем понятную ошибку, заказ пока не сохраняем.

Валидация проверяет правило. Очистка меняет строку и сама по себе не гарантирует правильные или безопасные данные.

В заказе введено "3", и приложение должно получить целое число 3; при "three" — объяснить ошибку. Расширение Filter предлагает две разные операции: валидация принимает или отклоняет значение по правилу, санитизация изменяет его символы. После этого всё ещё остаются правила заказа, права доступа и безопасная запись.

Запись filter_var($text, FILTER_VALIDATE_INT) вызывает функцию: значение переменной $text — первый аргумент, именованная константа выбирает проверку. Кавычки обозначают текст; = сохраняет результат, ; завершает команду. var_dump показывает значение и его тип; echo выводит текст, "\n" — перенос строки. Переменные; вызов и возврат.

В следующих шагах [...] создаёт массив; "ключ" => значение задаёт настройку. === сравнивает и значение, и тип; !== проверяет различие. if (...) { ... } выбирает ветку. foreach ($values as $value) перебирает значения. function name($value) { return ...; } объявляет повторяемое действие с локальным параметром. Массивы и ключи; ветвление и обход.

Что происходит с данными
  1. Человек ввёл "3"
  2. filter_var с FILTER_VALIDATE_INT
  3. Результат int(3) присвоен $quantity
  4. Приложение продолжает работу с количеством
Минимальная проверка с видимым результатомСамостоятельный пример
<?php
$quantity = filter_var('3', FILTER_VALIDATE_INT);
var_dump($quantity);
var_dump(filter_var('three', FILTER_VALIDATE_INT));
Ожидаемый результат
int(3)
bool(false)

Валидация не удаляла буквы, чтобы угадать число. Она вернула специальный результат отказа. $quantity содержит число; вывод — отдельное действие var_dump.

«Контроль на входе» здесь — аналогия для правил данных, а не гарантированный барьер безопасности всего приложения. Фильтр не знает владельца заказа и контекст вывода. Далее: источник значения и выбор функции, все варианты результата.

Карта всех функций и фильтров

Выбирайте функцию по тому, где данные уже находятся, и фильтр по договору поля. Группа input берёт оригинальный вход SAPI; группа var принимает переданное значение. Это две независимые оси выбора.

ЗадачаТочка входа
Одна переменная / поле уже декодированного JSONfilter_var
Группа полей массиваfilter_var_array
Одно исходное поле запросаfilter_input
Карта исходных полейfilter_input_array
Проверить факт наличия исходного имениfilter_has_var
Имена и их IDfilter_list + filter_id
Нужный результатФильтр и подробности
Целое / дробь / boolINT, FLOAT, BOOL
Формат своего кодаREGEXP или CALLBACK
Адрес назначенияURL, DOMAIN, EMAIL, IP, MAC
Изменение текстаRAW и флаги, HTML, email, URL, числа, слеши
Обслуживание старого кодаSTRING/STRIPPED, конфигурация

Результат, отказ, отсутствие, default и форма массива

Перед if выберите точный признак ошибки. false может означать корректное отключение, 0 — корректное количество, null — отсутствие или выбранную политику отказа. Одинаково выглядящий результат разных функций может иметь разный смысл.

ПравилоСмысл
filter_var, обычный валидаторНеверно → false; NULL_ON_FAILURE → null. Отсутствие переменной фильтр сам не определяет.
filter_input, без NULL_ON_FAILUREНет поля → null; неверное поле → false.
filter_input, с NULL_ON_FAILUREНет поля → false; неверное поле → null.
*_array и отсутствующий ключ картыadd_empty=true → null; false → ключ пропущен. NULL_ON_FAILURE не меняет значение отсутствующего ключа.
filter_input_array и пустой исходный наборВсего набора нет → null; это ещё не массив результатов.
Что происходит с данными
  1. "bad" не проходит INT
  2. Обычный режим → false
  3. Опция default=5 в другом вызове → 5
  4. Подстановка скрывает причину: решите, допустимо ли это
Выбрать подстановку осознанноСамостоятельный пример
<?php
var_dump(filter_var('bad', FILTER_VALIDATE_INT));
var_dump(filter_var('bad', FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE));
var_dump(filter_var('bad', FILTER_VALIDATE_INT, ['options' => ['default' => 5]]));
var_dump(filter_var('bad', FILTER_VALIDATE_INT, ['flags' => FILTER_NULL_ON_FAILURE, 'options' => ['default' => 5]]));
Ожидаемый результат
bool(false)
NULL
int(5)
int(5)

default меняет обычный результат отказа, а не делает исходный ввод корректным. Для необязательной настройки страницы это может быть договором; для количества оплачиваемого товара — потерей ошибки.

Проверить структуру и элементы отдельноСамостоятельный пример
<?php
var_dump(filter_var(['2'], FILTER_VALIDATE_INT));
echo json_encode(filter_var(['2', 'bad'], FILTER_VALIDATE_INT, FILTER_REQUIRE_ARRAY)), "\n";
echo json_encode(filter_var('bad', FILTER_VALIDATE_INT, FILTER_FORCE_ARRAY)), "\n";
var_dump(filter_var('2', FILTER_VALIDATE_INT, FILTER_REQUIRE_ARRAY));
Ожидаемый результат
bool(false)
[2,false]
[false]
bool(false)

FILTER_REQUIRE_SCALAR — стандартное требование скаляра. REQUIRE_ARRAY требует массив. FORCE_ARRAY оборачивает скалярный результат; он не доказывает успех и не заменяет проверку всех элементов.

Маска флагов — целое, | соединяет разрешённые для фильтра варианты: Битовые операции и маски. В PHP 8.5 добавлен THROW_ON_FAILURE; его договор, несовместимость с NULL и приоритет над default показаны в Filter\FilterFailedException.

SAPI-снимок: от запроса к переменной

SAPI — интерфейс, через который PHP получает данные окружения (веб-сервер, CLI и другие способы запуска). INPUT_GET/POST/COOKIE/ENV/SERVER выбирают снимок исходных данных. Значение в текущем суперглобальном массиве может позже отличаться; фильтры input не перечитывают ваши присваивания.

Что происходит с данными
  1. CLI не получил HTTP query
  2. Код присвоил $_GET["qty"]="8"
  3. filter_var видит текущее значение 8
  4. filter_input в этом CLI не получает поле qty
Почему присваивание не заменяет HTTP-тестСамостоятельный пример
<?php
$_GET['qty'] = '8';
var_dump(filter_var($_GET['qty'], FILTER_VALIDATE_INT));
var_dump(filter_input(INPUT_GET, 'qty', FILTER_VALIDATE_INT));
var_dump(filter_has_var(INPUT_GET, 'qty'));
Ожидаемый результат
int(8)
NULL
bool(false)

Этот опыт намеренно CLI. Настоящий сценарий исходного GET с изменением $_GET находится в карточке filter_input и проверяется HTTP-запросом.

Чистая функция принимает явные данныеСамостоятельный пример
<?php
function readQuantity($data) {
    if (!array_key_exists('qty', $data)) { return null; }
    return filter_var($data['qty'], FILTER_VALIDATE_INT);
}
var_dump(readQuantity(['qty' => '8']));
var_dump(readQuantity([]));
var_dump(readQuantity(['qty' => 'bad']));
Ожидаемый результат
int(8)
NULL
bool(false)

Для тестов бизнес-правила удобен обычный массив. array_key_exists проверяет наличие ключа, return немедленно отдаёт результат. Внешний HTTP-адаптер отдельно передаст нужные данные.

INPUT_GET — разобранная query, POST — разобранная форма, COOKIE — cookie, ENV — переменные среды, SERVER — данные сервера в доступном SAPI-снимке. Доступность ENV/SERVER зависит от SAPI и настроек; REQUEST_TIME добавляется позже и может отсутствовать. Сырые JSON-байты читаются отдельно: php://input и потоки. INPUT_SESSION/INPUT_REQUEST удалены в PHP 8.0, до этого не реализовывались. Ни COOKIE, ни SERVER-заголовок от клиента не становятся доверенными только из-за источника.

Целое: диапазон, ноль, пробелы и основания

Количество мест — целое от 0 до 50. FILTER_VALIDATE_INT проверяет запись и возвращает int, сохраняя различие между допустимым нулём и отказом false. Строка перед проверкой обрезается по краям (trim). Вход за пределами машинного int не становится большим точным числом.

ПравилоСмысл
Параметры optionsdefault — замена обычного отказа; min_range и max_range — целочисленные границы.
ФлагиFILTER_FLAG_ALLOW_OCTAL, FILTER_FLAG_ALLOW_HEX; общие структурные флаги и политика отказа.
Результатint; false/null/исключение в зависимости от политики. Для идентификатора больше PHP_INT_MAX сохраните проверенную строку, не float.
Что происходит с данными
  1. Текст " 12 "
  2. Краевые пробелы убраны; десятичная запись проверена
  3. Границы 0…50 соблюдены
  4. Возвращён int(12)
Диапазон с включёнными границамиСамостоятельный пример
<?php
$rule = ['options' => ['min_range' => 0, 'max_range' => 50]];
var_dump(filter_var(' 12 ', FILTER_VALIDATE_INT, $rule));
var_dump(filter_var('0', FILTER_VALIDATE_INT, $rule));
var_dump(filter_var('51', FILTER_VALIDATE_INT, $rule));
var_dump(filter_var('12.0', FILTER_VALIDATE_INT, $rule));
Ожидаемый результат
int(12)
int(0)
bool(false)
bool(false)

min_range и max_range включают границы. Десятичная дробная запись 12.0 не является допустимой записью целого для этого фильтра.

Принять номер страницы каталога и вычислить начало выборкиСамостоятельный пример
<?php
declare(strict_types=1);

$enteredPage = '3';
$page = filter_var($enteredPage, FILTER_VALIDATE_INT, [
    'options' => ['min_range' => 1, 'max_range' => 100],
]);
if ($page === false) {
    echo "Номер страницы должен быть от 1 до 100\n";
} else {
    $perPage = 20;
    $offset = ($page - 1) * $perPage;
    echo 'Страница ', $page, ': пропустить ', $offset, ', взять ', $perPage, " товаров\n";
}
Ожидаемый результат
Страница 3: пропустить 40, взять 20 товаров

После проверки $page — целое число, и его можно использовать в арифметике. Первые две страницы по 20 товаров занимают 40 позиций. В следующей SQL-ступени offset и perPage передают как целые параметры запроса, а не склеивают исходный пользовательский текст с SQL. Проверка диапазона одновременно защищает от отрицательного смещения и неограниченного размера пагинации; лимит выбирает приложение.

Формат числа — часть договора импортаСамостоятельный пример
<?php
var_dump(filter_var('010', FILTER_VALIDATE_INT));
var_dump(filter_var('010', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_OCTAL));
var_dump(filter_var('0x10', FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX));
var_dump(filter_var('bad', FILTER_VALIDATE_INT, ['options' => ['default' => 1]]));
Ожидаемый результат
bool(false)
int(8)
int(16)
int(1)

ALLOW_OCTAL разрешает восьмеричную запись, ALLOW_HEX — шестнадцатеричную. Для десятичного номера заказа с ведущими нулями обычно нужен строковый договор, а не изменение системы счисления.

Флаги оснований не нужны обычному полю количества. В проверенном PHP 8.5.11 ввод 0o37 тоже принимается как восьмеричный; это не проверка поведения фильтра во всех прежних версиях, поэтому выше использовано 010. Не добавляйте default к обязательному полю, если неверный текст должен стать видимой ошибкой. filter_var.

Логическое значение: false не равно «ошибка»

Настройка рассылки может быть включена, выключена либо испорчена. FILTER_VALIDATE_BOOL (имя с PHP 8.0; FILTER_VALIDATE_BOOLEAN — тот же фильтр) распознаёт несколько текстовых форм и возвращает bool. NULL_ON_FAILURE позволяет отдельно представить третье состояние — отказ.

ПравилоСмысл
optionsdefault — результат при обычном отказе.
Результатtrue или false; при отказе без NULL_ON_FAILURE тоже false.
Форма checkboxНеотмеченный checkbox часто вообще не отправляется. Сначала различите отсутствие, затем примените правило приложения.
Что происходит с данными
  1. Настройка "off"
  2. Булевый фильтр распознаёт отключённое состояние
  3. Возвращает false
  4. При "maybe" с NULL_ON_FAILURE возвращает null
Три разных результата настройкиСамостоятельный пример
<?php
var_dump(filter_var('off', FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
var_dump(filter_var('YES', FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
var_dump(filter_var('maybe', FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
Ожидаемый результат
bool(false)
bool(true)
NULL

Сравнение === null выявляет неверную настройку. if (!$value) ошибочно объединило бы выключение и ошибку.

Прочитать настройку уведомлений без неожиданного включенияСамостоятельный пример
<?php
declare(strict_types=1);

foreach (['off', 'YES', 'maybe'] as $raw) {
    $enabled = filter_var($raw, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE);
    if ($enabled === null) {
        echo $raw, ": ошибка настройки, уведомления не запускаем\n";
    } elseif ($enabled) {
        echo $raw, ": уведомления включены\n";
    } else {
        echo $raw, ": уведомления выключены\n";
    }
}
Ожидаемый результат
off: уведомления выключены
YES: уведомления включены
maybe: ошибка настройки, уведомления не запускаем

Такую строку можно получить из переменной окружения или файла конфигурации. Сначала проверяется null — неизвестное слово, затем истинность уже проверенного bool. Прямое (bool) 'off' дало бы true, потому что это непустая строка. Здесь текст «off» имеет согласованный смысл выключения. Пример печатает решение; самих уведомлений он не отправляет.

Если API разрешает только два словаСамостоятельный пример
<?php
function parseTwoWords($text) {
    if ($text === 'true') { return true; }
    if ($text === 'false') { return false; }
    return null;
}
var_dump(parseTwoWords('false'), parseTwoWords('yes'));
var_dump(filter_var('yes', FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
Ожидаемый результат
bool(false)
NULL
bool(true)

Собственный узкий протокол не обязан принимать весь набор встроенного фильтра. Пример показывает честную альтернативу, а не недостаток BOOL.

Числовое значение и текст записи — разные входыСамостоятельный пример
<?php
var_dump(filter_var(0x1, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
var_dump(filter_var('0x1', FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
var_dump(filter_var(1e0, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
var_dump(filter_var('1e0', FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE));
Ожидаемый результат
bool(true)
NULL
bool(true)
NULL

0x1 без кавычек — числовая запись единицы в PHP; 1e0 — float 1.0. В фильтр входят уже числа, которые превращаются в "1". Строки "0x1"/"1e0" не входят в разрешённый словарь BOOL. То же различие относится к двоичной/восьмеричной записи чисел 0 и 1.

Строки регистронезависимы и обрезаются по краям. true-формы: "1", "true", "on", "yes"; false-формы: "0", "false", "off", "no", пустая строка. Скаляры предварительно превращаются в строки: true/1/1.0 дают "1", false/null — пустую строку. Поэтому фильтр не доказывает, что в исходном JSON был именно bool. Для JSON-типа используйте is_bool. Отсутствие поля и политика отказа.

Дробь: разделитель, диапазон и точность

Датчик прислал "12.75", а оператор мог ввести "12,75". FILTER_VALIDATE_FLOAT возвращает float после проверки формата и обрезания краевых пробелов. Десятичный разделитель задаётся явно, не угадывается по стране пользователя.

Что происходит с данными
  1. Текст "12,75"
  2. decimal = ","
  3. Проверка формата
  4. float(12.75), а не текст с запятой
Разрешить конкретный десятичный разделительСамостоятельный пример
<?php
var_dump(filter_var('12,75', FILTER_VALIDATE_FLOAT));
var_dump(filter_var('12,75', FILTER_VALIDATE_FLOAT, ['options' => ['decimal' => ',']]));
var_dump(filter_var('1.5e2', FILTER_VALIDATE_FLOAT));
Ожидаемый результат
bool(false)
float(12.75)
float(150)

Научная запись разрешена самим валидатором FLOAT. FILTER_FLAG_ALLOW_SCIENTIFIC относится к числовому санитайзеру, не нужен здесь.

Проверить измерение датчика перед включением в отчётСамостоятельный пример
<?php
declare(strict_types=1);

$readings = ['22,5', '-3,2', 'warm'];
$options = ['options' => ['decimal' => ',', 'min_range' => -40, 'max_range' => 85]];
foreach ($readings as $raw) {
    $temperature = filter_var($raw, FILTER_VALIDATE_FLOAT, $options);
    if ($temperature === false) {
        echo $raw, ": измерение отклонено\n";
        continue;
    }
    echo $raw, ' -> ', $temperature, " °C; записать в отчёт\n";
}
Ожидаемый результат
22,5 -> 22.5 °C; записать в отчёт
-3,2 -> -3.2 °C; записать в отчёт
warm: измерение отклонено

Договор источника явно использует запятую. Фильтр превращает допустимую запись в число, а не просто заменяет запятую точкой. Метка «записать» показывает дальнейшее действие, сам файл пока не создаётся. Для датчика ещё важны время измерения, единицы и разумный диапазон. Поведение float не даёт точной десятичной арифметики для денег; здесь измерения имеют ограниченную точность изначально.

Отчёт с группировкой тысяч и пределомСамостоятельный пример
<?php
$options = [
    'flags' => FILTER_FLAG_ALLOW_THOUSAND,
    'options' => ['min_range' => 0, 'max_range' => 2000],
];
var_dump(filter_var('1,234.5', FILTER_VALIDATE_FLOAT, $options));
var_dump(filter_var('2,500.0', FILTER_VALIDATE_FLOAT, $options));
try {
    filter_var('1.2', FILTER_VALIDATE_FLOAT, ['options' => ['decimal' => '..']]);
} catch (ValueError $error) {
    echo "Разделитель должен быть одним символом\n";
}
Ожидаемый результат
float(1234.5)
bool(false)
Разделитель должен быть одним символом

min_range/max_range у FLOAT доступны с PHP 7.4. try/catch здесь показывает ошибку настроек программы, а не допустимую реакцию на любую сумму.

options: default, decimal (один символ; по умолчанию точка), min_range, max_range. ALLOW_THOUSAND разрешает запятую как разделитель групп; не превращает пробелы и любой международный формат в число. Деньги требуют отдельной модели точности: числа и округление. Обработка исключений: try/catch.

Собственный формат: FILTER_VALIDATE_REGEXP

Код склада должен иметь вид WH-42. Фильтр применяет PCRE-шаблон из options["regexp"] и при совпадении возвращает исходную строку. Чтобы проверить всю запись, шаблон должен сам задавать её границы.

Что происходит с данными
  1. "WH-42"
  2. Шаблон требует WH- и ровно две цифры
  3. Вся строка совпала
  4. Возвращена "WH-42"
Проверить весь код складаСамостоятельный пример
<?php
$options = ['options' => ['regexp' => '/\AWH-[0-9]{2}\z/']];
var_dump(filter_var('WH-42', FILTER_VALIDATE_REGEXP, $options));
var_dump(filter_var('prefix-WH-42', FILTER_VALIDATE_REGEXP, $options));
Ожидаемый результат
string(5) "WH-42"
bool(false)

/…/ ограничивает шаблон; \A и \z задают начало и конец всей строки, [0-9]{2} — ровно две ASCII-цифры. Это объяснение формата, не замена главе о регулярных выражениях.

Разделить неверный код склада и неизвестный складСамостоятельный пример
<?php
declare(strict_types=1);

$warehouses = ['WH-42' => 'Склад инструментов'];
$options = ['options' => ['regexp' => '/\AWH-[0-9]{2}\z/']];
foreach (['WH-42', 'WH-99', '42'] as $entered) {
    $code = filter_var($entered, FILTER_VALIDATE_REGEXP, $options);
    if ($code === false) {
        echo $entered, ": нужен формат WH-42\n";
    } elseif (!array_key_exists($code, $warehouses)) {
        echo $code, ": такого склада в справочнике нет\n";
    } else {
        echo $code, ': ', $warehouses[$code], "\n";
    }
}
Ожидаемый результат
WH-42: Склад инструментов
WH-99: такого склада в справочнике нет
42: нужен формат WH-42

Регулярное выражение проверяет только форму кода. Второе условие ищет уже корректный код в справочнике: правильная запись не доказывает существование склада. array_key_exists проверяет ключ массива, ! означает отрицание. В веб-приложении добавится третье правило — доступ текущего пользователя к найденному складу.

Совпадение внутри строки и полная проверка отличаютсяСамостоятельный пример
<?php
$text = 'ticket: WH-42';
var_dump(filter_var($text, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/WH-[0-9]{2}/']]));
var_dump(filter_var($text, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/\AWH-[0-9]{2}\z/']]));
Ожидаемый результат
string(13) "ticket: WH-42"
bool(false)

Фильтр возвращает весь вход, а не найденный фрагмент. Для извлечения групп нужен preg_match с массивом matches.

Настройка regexp обязательна; default доступен как обычная замена отказа. Некорректный PCRE — ошибка настройки, может дать предупреждение, а не нормальную ошибку поля. Не принимайте шаблон от посетителя без отдельного договора; сложные шаблоны и большой ввод требуют ограничений. PCRE, группы и стоимость поиска.

URL: синтаксис, протокол и назначение

В форме подключения поставщика нужен URL. FILTER_VALIDATE_URL проверяет синтаксис по RFC 2396, но не решает, разрешено ли серверу обращаться по адресу. Валидный file:// или адрес внутренней сети не становится допустимым адресом внешнего API.

Что происходит с данными
  1. "https://example.org/report"
  2. Проверка URL с PATH_REQUIRED
  3. Синтаксис и наличие пути подходят
  4. Строка возвращена; сетевого запроса не было
Путь и строка запроса задаются отдельными флагамиСамостоятельный пример
<?php
var_dump(filter_var('https://example.org/report', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));
var_dump(filter_var('https://example.org', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED));
var_dump(filter_var('https://example.org/?page=2', FILTER_VALIDATE_URL, FILTER_FLAG_QUERY_REQUIRED));
Ожидаемый результат
string(26) "https://example.org/report"
bool(false)
string(27) "https://example.org/?page=2"

PATH_REQUIRED требует путь, QUERY_REQUIRED — query-часть. Это не проверка существования страницы.

Подготовить HTTPS-ссылку в профиле автораСамостоятельный пример
<?php
declare(strict_types=1);

$entered = 'https://example.test/about?lang=ru&view=full';
$url = filter_var($entered, FILTER_VALIDATE_URL);
if ($url === false || parse_url($url, PHP_URL_SCHEME) !== 'https') {
    echo '<p>Укажите полный HTTPS-адрес.</p>';
} else {
    echo '<a href="', htmlspecialchars($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), '">Сайт автора</a>';
}
Ожидаемый результат
<a href="https://example.test/about?lang=ru&amp;view=full">Сайт автора</a>

Здесь URL нужен как ссылка в HTML, сервер сам по нему не обращается. Валидатор проверяет запись, parse_url выделяет схему, а htmlspecialchars защищает границы кавычечного HTML-атрибута. Амперсанд из query записан как & в исходнике страницы; браузер передаст обычный & при переходе. Домен example.test учебный, его существование не проверялось. Если по пользовательскому адресу должен обращаться сам сервер, нужен отдельный сетевой договор и защита от SSRF.

Дополнительное правило формы: только HTTPSСамостоятельный пример
<?php
function supplierUrl($text) {
    $url = filter_var($text, FILTER_VALIDATE_URL);
    if ($url === false || parse_url($url, PHP_URL_SCHEME) !== 'https') {
        return null;
    }
    return $url;
}
var_dump(supplierUrl('https://example.org/api'));
var_dump(supplierUrl('ftp://example.org/file'));
Ожидаемый результат
string(23) "https://example.org/api"
NULL

parse_url выделяет компонент, сам по себе не является полноценным валидатором. Это правило формы, а не завершённая защита сетевого клиента от SSRF: запросы здесь не выполняются.

Принятый URL ещё не разрешённый сетевой адресСамостоятельный пример
<?php
var_dump(filter_var('unknown-app://example', FILTER_VALIDATE_URL) !== false);
var_dump(filter_var('http://127.0.0.1', FILTER_VALIDATE_URL) !== false);
var_dump(filter_var('mailto:team@example.org', FILTER_VALIDATE_URL) !== false);
Ожидаемый результат
bool(true)
bool(true)
bool(true)

Это только проверки строк. Не выполняется ни запрос на localhost, ни отправка письма, ни запуск неизвестной схемы.

Возвращает строку либо отказ; option default. Для схем кроме http/https содержимое host проверяется очень мягко; само имя схемы не ограничено известными протоколами. Наличие host обычно требуется, но mailto/news/file — исключения. Loopback допускается. Валидатор рассчитан на ASCII: международное доменное имя сначала приводят к согласованной IDN-форме средствами intl, с обработкой ошибки. Перед реальным запросом нужны правила хоста/порта, разрешения DNS/IP и перенаправлений; одной проверки https недостаточно. FILTER_FLAG_SCHEME_REQUIRED и FILTER_FLAG_HOST_REQUIRED устарели в 7.3 и удалены в 8.0: схема и host (для большинства схем) требуются самим валидатором. Сетевой клиент и ошибки интеграции; границы доверия.

Домен и hostname — не один уровень проверки

Для адреса SMTP-сервера нужна допустимая форма имени хоста. FILTER_VALIDATE_DOMAIN проверяет доменную форму; FILTER_FLAG_HOSTNAME сужает символы меток до букв/цифр/дефиса и не позволяет дефис по краям метки. Без этого флага проверка гораздо мягче и может пропускать пробелы, метасимволы, управляющие байты и даже NUL.

Что происходит с данными
  1. "smtp.example.org"
  2. DOMAIN + HOSTNAME проверяет метки
  3. Длины и символы подходят
  4. Строка принята без запроса DNS
Явно выбрать правила hostnameСамостоятельный пример
<?php
var_dump(filter_var('smtp.example.org', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME));
var_dump(filter_var('bad host', FILTER_VALIDATE_DOMAIN));
var_dump(filter_var('bad host', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME));
Ожидаемый результат
string(16) "smtp.example.org"
string(8) "bad host"
bool(false)

Мягкое DOMAIN не является способом запретить опасные символы в произвольном контексте. HOSTNAME нужно выбирать по назначению поля.

Отличить имя почтового хоста от полного URLСамостоятельный пример
<?php
declare(strict_types=1);

foreach (['smtp.example.test', 'https://smtp.example.test', 'bad host'] as $raw) {
    $host = filter_var($raw, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
    if ($host === false) {
        echo $raw, ": требуется имя хоста без протокола и пробелов\n";
    } else {
        echo 'Хост: ', $host, "; порт задаётся отдельным полем\n";
    }
}
Ожидаемый результат
Хост: smtp.example.test; порт задаётся отдельным полем
https://smtp.example.test: требуется имя хоста без протокола и пробелов
bad host: требуется имя хоста без протокола и пробелов

Форма настройки почтового клиента часто просит host, а человек вставляет полный адрес из браузера. Сообщение объясняет различие: smtp.example.test — имя, https://... — URL. Флаг HOSTNAME нужен для строгого набора символов меток. Пример не устанавливает SMTP-соединение: DNS, доступность порта, TLS и учётные данные проверяются на следующей ступени.

Проверить границы метки и localhostСамостоятельный пример
<?php
$tooLong = str_repeat('a', 64) . '.example';
var_dump(filter_var($tooLong, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME));
var_dump(filter_var('localhost', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME));
var_dump(filter_var('-mail.example', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME));
Ожидаемый результат
bool(false)
string(9) "localhost"
bool(false)

str_repeat создаёт тестовую метку. Отсутствие точки само по себе не запрещено этим фильтром; требование публичного домена — отдельная политика.

option default; результат строка или отказ. RFC-основа: 952, 1034, 1035, 1123, 2732, 2181. Метка ограничена 63 байтами, домен — 253 (254 с завершающей точкой); это байты ASCII-представления. Не проверяет регистрацию, DNS и владение. Для интернациональных имён сначала обоснованная IDN-нормализация, не удаление кириллицы.

Email: форма адреса и подтверждение получателя

Сайт должен принять адрес доставки уведомлений. FILTER_VALIDATE_EMAIL проверяет поддерживаемую часть addr-spec RFC 822 и возвращает строку; он не отправляет письмо и не подтверждает владение ящиком. Комментарии, переносы и домен без точки не входят в поддерживаемую форму.

Что происходит с данными
  1. "team+orders@example.org"
  2. Проверяется синтаксис адреса
  3. Возвращена та же строка
  4. Подтверждение ящика — следующий отдельный процесс
Корректная форма и явный отказСамостоятельный пример
<?php
var_dump(filter_var('team+orders@example.org', FILTER_VALIDATE_EMAIL));
var_dump(filter_var('Team <team@example.org>', FILTER_VALIDATE_EMAIL));
var_dump(filter_var('team@localhost', FILTER_VALIDATE_EMAIL));
Ожидаемый результат
string(23) "team+orders@example.org"
bool(false)
bool(false)

Поле хранит один адрес, а не заголовок письма с отображаемым именем. Не пытайтесь автоматически «починить» такой ввод удалением знаков.

Вернуть понятное сообщение для поля получателяСамостоятельный пример
<?php
declare(strict_types=1);

foreach (['buyer@example.test', 'buyer at example.test', 'Buyer <buyer@example.test>'] as $raw) {
    $email = filter_var($raw, FILTER_VALIDATE_EMAIL);
    if ($email === false) {
        echo 'Не принято: ', $raw, ". Введите один адрес без имени и скобок.\n";
    } else {
        echo 'Формат адреса принят: ', $email, "; владение ещё не подтверждено\n";
    }
}
Ожидаемый результат
Формат адреса принят: buyer@example.test; владение ещё не подтверждено
Не принято: buyer at example.test. Введите один адрес без имени и скобок.
Не принято: Buyer <buyer@example.test>. Введите один адрес без имени и скобок.

Проверка возвращает адрес, пригодный для следующего шага приложения, или false. Формат поля заранее определён как один адрес, а не строка заголовка письма с именем. Этот CLI-пример показывает исходный ввод как текст; на HTML-странице его обязательно кодируют через htmlspecialchars. Не удаляем случайные символы и не отправляем письмо «исправленному» получателю. Подтверждение адреса требует отдельного ограниченного по времени кода или ссылки.

Unicode в локальной части и в домене различаетсяСамостоятельный пример
<?php
var_dump(filter_var('почта@example.org', FILTER_VALIDATE_EMAIL) !== false);
var_dump(filter_var('почта@example.org', FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE) !== false);
var_dump(filter_var('mail@пример.рф', FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE) !== false);
Ожидаемый результат
bool(false)
bool(true)
bool(false)

EMAIL_UNICODE с PHP 7.1 разрешает Unicode только в части перед @. Возможности отправляющей системы и принимающего сервера нужно учитывать отдельно.

options: default; флаг EMAIL_UNICODE. Для IDN-домена требуется согласованное ASCII-преобразование intl и отдельный контроль ошибки. DNS-проверка не доказывает наличие конкретного ящика; подтверждение отправляют отдельным механизмом. FILTER_SANITIZE_EMAIL лишь удаляет символы и может получить другой адрес. Почему очистка email меняет получателя.

IP: версия адреса, частные диапазоны и global

В настройках интеграции принимается IP. FILTER_VALIDATE_IP проверяет IPv4/IPv6 без порта, CIDR-маски или квадратных скобок URL. Ограничения диапазона выбираются явно; синтаксически верный адрес может принадлежать внутренней сети.

Что происходит с данными
  1. "192.168.1.5"
  2. IPV4 подтверждает форму
  3. NO_PRIV_RANGE запрещает частный диапазон
  4. Результат false при этой политике
Форма и частный диапазонСамостоятельный пример
<?php
var_dump(filter_var('192.168.1.5', FILTER_VALIDATE_IP, FILTER_FLAG_IPV4));
var_dump(filter_var('192.168.1.5', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE));
var_dump(filter_var('2001:db8::1', FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
var_dump(filter_var('192.168.1.5:80', FILTER_VALIDATE_IP));
Ожидаемый результат
string(11) "192.168.1.5"
bool(false)
string(11) "2001:db8::1"
bool(false)

Документационный IPv6 используется только как текстовый пример, сетевых запросов нет. IPV4/IPV6 ограничивают семейство, не доступность.

Принять адрес локального датчика в инвентарный списокСамостоятельный пример
<?php
declare(strict_types=1);

$enteredAddresses = ['192.168.10.15', '192.168.10.15:8080', '999.1.2.3'];
foreach ($enteredAddresses as $raw) {
    $address = filter_var($raw, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
    if ($address === false) {
        echo $raw, ": неверная запись IPv4; порт укажите отдельно\n";
    } else {
        echo 'Карточка датчика: IP ', $address, "\n";
    }
}
Ожидаемый результат
Карточка датчика: IP 192.168.10.15
192.168.10.15:8080: неверная запись IPv4; порт укажите отдельно
999.1.2.3: неверная запись IPv4; порт укажите отдельно

Частный адрес здесь уместен: приложение ведёт оборудование внутренней сети. Добавление NO_PRIV_RANGE сломало бы задачу, поэтому набор флагов выбирают по назначению. Проверка не ищет датчик и не соединяется с ним. Для журнала запросов адрес клиента обычно берут из REMOTE_ADDR; произвольному X-Forwarded-For доверять нельзя без настройки известных прокси.

Два запрета не равны признаку globalСамостоятельный пример
<?php
$shared = '100.64.0.1';
$twoFlags = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
var_dump(filter_var($shared, FILTER_VALIDATE_IP, $twoFlags));
var_dump(filter_var($shared, FILTER_VALIDATE_IP, FILTER_FLAG_GLOBAL_RANGE));
Ожидаемый результат
string(10) "100.64.0.1"
bool(false)

GLOBAL_RANGE доступен с PHP 8.2 и следует global-атрибуту специальных диапазонов. Нельзя описывать его как простую замену двух предыдущих флагов.

option default. NO_PRIV_RANGE исключает IPv4 10/8, 172.16/12, 192.168/16 и IPv6 fc00::/7. NO_RES_RANGE по текущему договору исключает IPv4 0/8, 169.254/16, 127/8, 240/4 и IPv6 ::1/128, ::/128, ::ffff:0:0/96, fe80::/10. Это конкретные наборы, не обещание блокировать все нежелательные для приложения адреса. Проверка строки не предотвращает DNS-rebinding и не доказывает безопасность переадресации. Безопасность сетевых назначений.

MAC: формат идентификатора оборудования

В инвентарной карточке нужно сохранить MAC-адрес. FILTER_VALIDATE_MAC проверяет формат, но не подтверждает существование адаптера или уникальность оборудования. Результат — исходная строка, не каноническая запись.

Что происходит с данными
  1. "02:11:22:33:44:55"
  2. Проверка формы MAC
  3. Строка принята
  4. Для поиска отдельно выбираем единый формат хранения
Проверить MAC и отклонить посторонний текстСамостоятельный пример
<?php
var_dump(filter_var('02:11:22:33:44:55', FILTER_VALIDATE_MAC));
var_dump(filter_var('device-42', FILTER_VALIDATE_MAC));
Ожидаемый результат
string(17) "02:11:22:33:44:55"
bool(false)

Фильтр отвечает на вопрос формы строки, а не проводит опрос сети.

Нормализовать уже проверенный адрес для карточкиСамостоятельный пример
<?php
$value = filter_var('02-AA-BB-CC-DD-EE', FILTER_VALIDATE_MAC);
if ($value === false) {
    echo "Неверный MAC\n";
} else {
    echo strtolower(str_replace('-', ':', $value)), "\n";
}
Ожидаемый результат
02:aa:bb:cc:dd:ee

str_replace заменяет разделитель, strtolower — регистр ASCII. Нормализация идёт после успешной проверки и относится к выбранным двоеточию/дефису; не обещает поддержку любой внешней нотации.

option default; отказ по общей политике. MAC может быть локально назначенным или изменяемым; для авторизации пользователя/устройства одного такого поля недостаточно. filter_var.

RAW и флаги удаления/кодирования байтов

FILTER_DEFAULT — псевдоним FILTER_UNSAFE_RAW. Без флагов он оставляет текст как есть: название filter_var не означает, что любое значение стало проверенным. Для ограниченного технического поля можно явно удалить конкретные управляющие символы, сохранив исходник для диагностики.

ПравилоСмысл
FILTER_FLAG_STRIP_LOW / FILTER_FLAG_STRIP_HIGHУдалить байты ASCII-кода ниже 32 / выше 127.
FILTER_FLAG_STRIP_BACKTICKУдалить обратную кавычку `.
FILTER_FLAG_ENCODE_LOW / FILTER_FLAG_ENCODE_HIGHЗакодировать соответствующие байты числовыми сущностями там, где флаг поддержан.
FILTER_FLAG_ENCODE_AMPЗакодировать амперсанд &.
FILTER_FLAG_NO_ENCODE_QUOTESНе кодировать кавычки в поддерживающем это фильтре; SPECIAL_CHARS его игнорирует.
FILTER_FLAG_EMPTY_STRING_NULLПустой результат санитайзера представить null.
FILTER_FLAG_NONEНулевая маска; никаких дополнительных флагов.
Что происходит с данными
  1. Текст "A" + перевод строки + "B"
  2. RAW без флагов сохраняет строку
  3. RAW + STRIP_LOW удаляет байт 10
  4. Получаем "AB"; валидность по бизнес-правилу ещё не проверена
Сохранить либо явно удалить перевод строкиСамостоятельный пример
<?php
$text = "A\nB";
echo json_encode(filter_var($text)), "\n";
echo json_encode(filter_var($text, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW)), "\n";
var_dump(FILTER_DEFAULT === FILTER_UNSAFE_RAW);
Ожидаемый результат
"A\nB"
"AB"
bool(true)

json_encode делает невидимый перевод строки видимым как \n. По умолчанию RAW ничего не удалил.

Пустой результат и текстовый ноль различаютсяСамостоятельный пример
<?php
var_dump(filter_var('', FILTER_UNSAFE_RAW, FILTER_FLAG_EMPTY_STRING_NULL));
var_dump(filter_var('0', FILTER_UNSAFE_RAW, FILTER_FLAG_EMPTY_STRING_NULL));
echo filter_var('A&B', FILTER_UNSAFE_RAW, FILTER_FLAG_ENCODE_AMP), "\n";
echo filter_var('`name`', FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_BACKTICK), "\n";
Ожидаемый результат
NULL
string(1) "0"
A&#38;B
name

EMPTY_STRING_NULL относится к пустой строке после санитайзера, не к любому falsey-значению. ENCODE_AMP создаёт числовую сущность, это не общее экранирование HTML.

STRIP_HIGH и ENCODE_HIGH работают с байтами выше 127: русские UTF-8-буквы состоят из таких байтов, поэтому удаление HIGH уничтожит текст. Не используйте это как «защиту Unicode». Флаг действует только там, где фильтр его поддерживает; одинаковое числовое значение разных флагов не делает их взаимозаменяемыми. HTML-контекст.

HTML: SPECIAL_CHARS и FULL_SPECIAL_CHARS

Комментарий <b>Sale</b> нужно показать как текст страницы. Это задача кодирования при выводе, а не изменения исходного комментария в базе. SPECIAL_CHARS кодирует кавычки, угловые скобки, амперсанд и управляющие ASCII-байты числовыми сущностями; FULL_SPECIAL_CHARS соответствует htmlspecialchars с ENT_QUOTES и учитывает default_charset.

Что происходит с данными
  1. Исходный текст <b>Sale</b>
  2. Кодирование для HTML-текста
  3. В ответе &lt;b&gt;Sale&lt;/b&gt;
  4. Браузер показывает угловые скобки как текст
Флаг кавычек действует по-разномуСамостоятельный пример
<?php
$text = '"<b>"';
echo filter_var($text, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES), "\n";
echo filter_var($text, FILTER_SANITIZE_FULL_SPECIAL_CHARS), "\n";
echo filter_var($text, FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES), "\n";
Ожидаемый результат
&#34;&#60;b&#62;&#34;
&quot;&lt;b&gt;&quot;
"&lt;b&gt;"

SPECIAL_CHARS игнорирует NO_ENCODE_QUOTES. Для значения HTML-атрибута отключение кодирования кавычек может нарушить границы атрибута.

Вывести комментарий как текст, сохранив написанные скобкиСамостоятельный пример
<?php
declare(strict_types=1);

$comment = '<b>Кисть</b> & валик';
$safeText = filter_var($comment, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
echo '<p>', $safeText, '</p>';
Ожидаемый результат
<p>&lt;b&gt;Кисть&lt;/b&gt; &amp; валик</p>

В PHP-выводе появились сущности &lt; и &amp;. Браузер прочитает их как видимые символы < и &, а не начнёт выполнять теги из комментария. Ниже показан именно такой результат. default_charset должен соответствовать входной строке, здесь UTF-8. Исходный $comment сохраняется; кодирование применено только при выводе. Для явно выбранной политики обработки повреждённых байтов удобнее htmlspecialchars с ENT_SUBSTITUTE.

Так выглядит результат в браузере

<b>Кисть</b> & валик

Неверная кодировка и явная политика заменыСамостоятельный пример
<?php
ini_set('default_charset', 'UTF-8');
$broken = "A\xFFB";
var_dump(filter_var($broken, FILTER_SANITIZE_FULL_SPECIAL_CHARS));
echo bin2hex(htmlspecialchars($broken, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')), "\n";
Ожидаемый результат
string(0) ""
41efbfbd42

При неверной UTF-8-последовательности FULL_SPECIAL_CHARS отвергает всю строку в пустую. У htmlspecialchars с явно заданным ENT_SUBSTITUTE другая политика: некорректный байт заменён U+FFFD (ef bf bd в UTF-8). bin2hex показывает байты; настройка меняется только внутри этого процесса.

SPECIAL_CHARS поддерживает STRIP_LOW/HIGH/BACKTICK и ENCODE_HIGH; FULL_SPECIAL_CHARS — описанную настройку кавычек и charset. Ни один из этих примеров не кодирует JavaScript, CSS, SQL или URL. Для HTML-текста и кавычечного атрибута обычно понятнее явный htmlspecialchars(..., ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8") на границе вывода. Контекст вывода и XSS.

Очистка email может поменять получателя

FILTER_SANITIZE_EMAIL удаляет символы вне разрешённого набора. Это полезно лишь при явно согласованной операции редактирования текста; для адреса получателя незаметное исправление опасно: человек отправил один текст, а приложение создало другой адрес.

Что происходит с данными
  1. Ввод "a b@example.org"
  2. SANITIZE_EMAIL удаляет пробел
  3. Получено "ab@example.org"
  4. Адрес изменился; исходный ввод лучше отклонить
Увидеть изменение адресаСамостоятельный пример
<?php
$entered = 'a b@example.org';
echo filter_var($entered, FILTER_SANITIZE_EMAIL), "\n";
var_dump(filter_var($entered, FILTER_VALIDATE_EMAIL));
Ожидаемый результат
ab@example.org
bool(false)

Оба вызова выполняют свой договор. Для формы получателя нужен второй: первый не знает, кому хотел написать человек.

Разрешённые символы не гарантируют адресСамостоятельный пример
<?php
$cleaned = filter_var('@@example.org', FILTER_SANITIZE_EMAIL);
echo $cleaned, "\n";
var_dump(filter_var($cleaned, FILTER_VALIDATE_EMAIL));
Ожидаемый результат
@@example.org
bool(false)

Два @ остаются, потому что каждый символ разрешён. Санитайзер не проверяет взаимное положение символов.

Остаются ASCII-латиница a–z/A–Z, цифры и символы !#$%&'*+-=?^_`{|}~@.[]. Кириллица будет удалена, даже если приложение умеет международную почту. Результат — строка, а не подтверждение адреса. Валидация email.

Очистка URL и URL-кодирование — разные задачи

FILTER_SANITIZE_URL удаляет символы вне URL-набора; FILTER_SANITIZE_ENCODED кодирует строку как компонент URL. Первый не выбирает безопасный протокол, второй не предназначен для кодирования целого адреса с его разделителями.

Что происходит с данными
  1. Поисковый текст "red shoes"
  2. SANITIZE_ENCODED кодирует пробел
  3. "red%20shoes" — компонент запроса
  4. Добавляем после ?q= в заранее заданный адрес
Закодировать значение, сохранив структуру адресаСамостоятельный пример
<?php
$term = 'red shoes';
echo '/search?q=', filter_var($term, FILTER_SANITIZE_ENCODED), "\n";
echo filter_var('https://example.org/?q=x', FILTER_SANITIZE_ENCODED), "\n";
Ожидаемый результат
/search?q=red%20shoes
https%3A%2F%2Fexample.org%2F%3Fq%3Dx

Во второй строке пропали служебные разделители адреса: они закодированы как обычные данные. Для нескольких параметров удобнее http_build_query с явно выбранной политикой кодирования.

Удалить пробел не значит проверить протоколСамостоятельный пример
<?php
echo filter_var('https://example.org/a b', FILTER_SANITIZE_URL), "\n";
echo filter_var('javascript:alert(1)', FILTER_SANITIZE_URL), "\n";
var_dump(filter_var('javascript:alert(1)', FILTER_VALIDATE_URL));
Ожидаемый результат
https://example.org/ab
javascript:alert(1)
bool(false)

Пример только выводит текст в CLI, не выполняет JavaScript. Санитайзер сохранил опасную для href схему; нужные правила протокола нельзя заменить удалением символов.

URL-санитайзер оставляет ASCII-латиницу, цифры и $-_.+!*'(),{}|\^~[]`<>#%";/?:@&=. Этот набор включает кавычки и угловые скобки: результат нельзя без HTML-кодирования вставлять в атрибут. ENCODED поддерживает STRIP/ENCODE-флаги для указанных байтов; он не валидирует назначение адреса. Проверка URL и дальнейшая политика.

Числовой санитайзер возвращает текст и может менять число

Импорт с единицами измерения иногда требует явно согласованной очистки, но санитайзер не знает смысл строки. NUMBER_INT оставляет цифры и знаки +/−; NUMBER_FLOAT без флагов делает то же самое, удаляя десятичную точку.

Что происходит с данными
  1. "12.50"
  2. NUMBER_FLOAT без ALLOW_FRACTION
  3. Точка удалена
  4. "1250" — другая строка и другое число
Сначала увидеть опасную смену значенияСамостоятельный пример
<?php
var_dump(filter_var('12.50', FILTER_SANITIZE_NUMBER_FLOAT));
var_dump(filter_var('12.50', FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
var_dump(filter_var('++12 kg', FILTER_SANITIZE_NUMBER_INT));
var_dump(filter_var('++12', FILTER_VALIDATE_INT));
Ожидаемый результат
string(4) "1250"
string(5) "12.50"
string(4) "++12"
bool(false)

Ни FLOAT в имени, ни сохранённые цифры не превращают результат в число. Если неправильный ввод должен стать ошибкой, сразу используйте VALIDATE.

Разрешённые символы для заданного формата импортаСамостоятельный пример
<?php
$flags = FILTER_FLAG_ALLOW_FRACTION | FILTER_FLAG_ALLOW_THOUSAND | FILTER_FLAG_ALLOW_SCIENTIFIC;
$text = filter_var('value=1,250.5e2 units', FILTER_SANITIZE_NUMBER_FLOAT, $flags);
echo $text, "\n";
var_dump(filter_var($text, FILTER_VALIDATE_FLOAT, FILTER_FLAG_ALLOW_THOUSAND));
Ожидаемый результат
e1,250.5e2
bool(false)

Буква e из слова value тоже сохранена. Даже очистка с правильными флагами не умеет извлекать число из произвольного предложения: нужен известный формат, парсер или регулярное выражение.

ALLOW_FRACTION сохраняет точку, ALLOW_THOUSAND — запятую, ALLOW_SCIENTIFIC — e/E. Эти флаги соединяют через |. NUMBER_INT не принимает такие флаги как способ разрешить дробь. Для поля цены не удаляйте произвольно символы: можно превратить ошибку пользователя в иной заказ. Проверка дробной записи; Извлечение по точному шаблону.

ADD_SLASHES: точный механизм и предел применимости

FILTER_SANITIZE_ADD_SLASHES с PHP 7.3 применяет addslashes: добавляет обратный слеш перед одинарной/двойной кавычкой, обратным слешем и нулевым байтом. Это конкретное строковое преобразование, а не универсальная подготовка данных для SQL.

Что происходит с данными
  1. Имя "O'Neil"
  2. ADD_SLASHES ставит \ перед апострофом
  3. "O\'Neil"
  4. Для SQL выбираем параметры запроса, не эту строку
Увидеть добавленный символСамостоятельный пример
<?php
$name = "O'Neil";
echo filter_var($name, FILTER_SANITIZE_ADD_SLASHES), "\n";
var_dump(filter_var($name, FILTER_SANITIZE_ADD_SLASHES) === addslashes($name));
Ожидаемый результат
O\'Neil
bool(true)

При чтении legacy-кода это помогает узнать источник добавленных слешей. В форме имя O'Neil само по себе допустимо и не нуждается в изменении для хранения.

Нулевой байт, слеш и обе кавычкиСамостоятельный пример
<?php
$text = "\0\\\"'";
echo bin2hex(filter_var($text, FILTER_SANITIZE_ADD_SLASHES)), "\n";
Ожидаемый результат
5c305c5c5c225c27

bin2hex показывает точные байты: \0, \\, \" и \'. Это не проверка кодировки, длины или допустимости содержимого.

FILTER_SANITIZE_MAGIC_QUOTES был псевдонимом: deprecated с PHP 7.3, удалён с 8.0. В актуальном коде нельзя обращаться к удалённой константе. Для SQL нужны подготовленные запросы и параметры: PDO и связанные параметры. Для вывода в HTML — другой контекст, HTML-кодирование.

STRING/STRIPPED: понять старый код и заменить по назначению

FILTER_SANITIZE_STRING и его псевдоним FILTER_SANITIZE_STRIPPED устарели с PHP 8.1. Они удаляют теги и кодируют кавычки; способ удаления не эквивалентен strip_tags. Старую строку нельзя механически заменить одной «очисткой на все случаи»: сначала найдите, что приложение хотело получить.

Что происходит с данными
  1. В базе хранится "<b>New</b>"
  2. Требование: показать как текст HTML
  3. htmlspecialchars кодирует на выходе
  4. Исходная строка остаётся пригодной для других представлений
Замена для HTML-текста с сохранением исходникаСамостоятельный пример
<?php
$original = '<b>New</b>';
echo htmlspecialchars($original, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
echo $original, "\n";
Ожидаемый результат
&lt;b&gt;New&lt;/b&gt;
<b>New</b>

CLI печатает строки. На странице первый результат покажет скобки, а второй без кодирования будет HTML-разметкой. Исходник не изменился.

Иное требование: ограниченный текст без теговСамостоятельный пример
<?php
$description = '<p>New <strong>box</strong></p>';
$plain = strip_tags($description);
echo $plain, "\n";
echo htmlspecialchars($plain, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
Ожидаемый результат
New box
New box

strip_tags уместен только если удаление разметки — согласованное правило. Это не полноценный HTML-парсер/санитайзер и не точный эмулятор старого фильтра.

Историческая запись, которую не переносим в новый кодФрагмент или команда
filter_var($input, FILTER_SANITIZE_STRING);
filter_var($input, FILTER_SANITIZE_STRIPPED, FILTER_FLAG_NO_ENCODE_QUOTES);

Исторический фрагмент. PHP 8.1+ сообщает deprecation для обеих констант. Он не исполняется как рекомендуемое решение; нет скрытого подавления диагностики через @.

Старый STRING поддерживал STRIP/ENCODE-флаги, NO_ENCODE_QUOTES отключал кодирование кавычек. RAW как механическая замена снимет прежнее преобразование; FULL_SPECIAL_CHARS закодирует теги вместо удаления. Разницу нужно проверить на сохранённых примерах приложения. Это миграция поведения, а не простая замена имени.

FILTER_CALLBACK: своё правило и произвольный результат

Импортёр должен привести артикул к верхнему регистру и принять только два известных кода. Callback — переданная функция: фильтр вызывает её со строкой, а её return становится результатом без дополнительной оценки успешности. options здесь содержит сам callable, а не массив настроек обычного валидатора.

Что происходит с данными
  1. " box "
  2. filter_var вызывает normalizeSku(" box ")
  3. trim → strtoupper → проверка разрешённых кодов
  4. return "BOX" возвращается вызывающему коду
Собственное повторяемое правилоСамостоятельный пример
<?php
function normalizeSku($value) {
    $sku = strtoupper(trim($value));
    return in_array($sku, ['BOX', 'BAG'], true) ? $sku : null;
}
$options = ['options' => 'normalizeSku'];
var_dump(filter_var(' box ', FILTER_CALLBACK, $options));
var_dump(filter_var('other', FILTER_CALLBACK, $options));
Ожидаемый результат
string(3) "BOX"
NULL

Имя функции в строке — callable. trim убирает краевые пробелы, strtoupper меняет ASCII-регистр, условие ? A : B выбирает результат. null здесь вернула сама функция, не флаг фильтра.

После своего правила получить товар из каталогаСамостоятельный пример
<?php
declare(strict_types=1);

function normalizeCatalogSku($raw) {
    return strtoupper(trim($raw));
}
$catalog = ['BR-41' => 'Кисть широкая', 'HM-10' => 'Молоток'];
$rule = ['options' => 'normalizeCatalogSku'];
foreach ([' br-41 ', ' hm-10 ', 'xx-99'] as $raw) {
    $sku = filter_var($raw, FILTER_CALLBACK, $rule);
    echo $sku, ': ', $catalog[$sku] ?? 'товар не найден', "\n";
}
Ожидаемый результат
BR-41: Кисть широкая
HM-10: Молоток
XX-99: товар не найден

FILTER_CALLBACK вызывает названную функцию, а её return становится $sku. trim убирает краевые пробелы, strtoupper приводит ASCII-код к договорённому регистру. Проверка существования товара идёт после нормализации: вызов callback сам не знает каталога. ?? выбирает запасное сообщение, если ключ отсутствует. Для одной такой операции прямой normalizeCatalogSku($raw) проще; callback полезен, когда то же правило включено в общую карту фильтров.

То же правило по элементам массиваСамостоятельный пример
<?php
$options = ['options' => 'strtoupper', 'flags' => FILTER_REQUIRE_ARRAY];
echo json_encode(filter_var(['box', 'bag'], FILTER_CALLBACK, $options)), "\n";
var_dump(filter_var('box', FILTER_CALLBACK, $options));
Ожидаемый результат
["BOX","BAG"]
bool(false)

Структурные флаги действуют с PHP 8.1.25 / 8.2.12; во всех прежних версиях они игнорировались для CALLBACK. Верхний скаляр отклонён до вызова strtoupper.

false callback не превращается в отказ валидатораСамостоятельный пример
<?php
function rejectedValue($value) { return false; }
var_dump(filter_var('x', FILTER_CALLBACK, ['options' => 'rejectedValue', 'flags' => FILTER_NULL_ON_FAILURE]));
var_dump(filter_var('x', FILTER_CALLBACK, ['options' => 'rejectedValue', 'flags' => FILTER_THROW_ON_FAILURE]));
Ожидаемый результат
bool(false)
bool(false)

Вторая строка требует PHP 8.5. Флаги не интерпретируют false, возвращённый callback. Его собственное исключение, напротив, распространяется обычным способом.

Сигнатура callback по договору function (string $value): mixed: строковый вход, произвольный результат. REQUIRE_ARRAY/FORCE_ARRAY/REQUIRE_SCALAR и NULL_ON_FAILURE влияют на структурные ошибки; специфические флаги валидаторов игнорируются. Неверный callable — ошибка конфигурации (TypeError); время выполнения callback входит в стоимость фильтрации каждого элемента. Для отдельного собственного правила прямой вызов normalizeSku проще; CALLBACK полезен в общей карте filter_var_array. Функции как значения.

Установка, INI и версии при сопровождении

Filter обычно включён по умолчанию; при собственной сборке PHP его можно отключить --disable-filter. Проверка расширения помогает сразу отличить отсутствие функции от неверного правила данных. Не меняйте глобальную обработку всего входа ради одного поля.

ПравилоСмысл
filter.defaultСтрока; по умолчанию unsafe_raw. INI_PERDIR; настройка deprecated с PHP 8.1. Имя фильтра из filter_list/id. В legacy-конфигурации влияет на GET/POST/COOKIE/REQUEST/SERVER; filter_input сохраняет доступ к исходному входу.
filter.default_flagsINI_PERDIR; таблица INI PHP.net указывает default NULL, текст уточняет исходную совместимую настройку FILTER_FLAG_NO_ENCODE_QUOTES. Для legacy full_special_chars устанавливают 0, если нужно кодирование кавычек. Это не рекомендация глобальной очистки.
Где менять INI_PERDIRphp.ini, серверная/директорная конфигурация согласно SAPI; не обычный ini_set для произвольной смены внутри скрипта.
default_charsetКодировка FULL_SPECIAL_CHARS; невалидная последовательность отвергает строку целиком.
Исторические именаSTRING/STRIPPED deprecated 8.1; MAGIC_QUOTES deprecated 7.3, удалён 8.0; SCHEME_REQUIRED/HOST_REQUIRED deprecated 7.3, удалены 8.0; INPUT_SESSION/REQUEST удалены 8.0.
Что происходит с данными
  1. Текущий процесс PHP
  2. extension_loaded("filter")
  3. Расширение доступно
  4. Можно проверять нужное правило и его версию
Проверить окружение без печати всей конфигурацииСамостоятельный пример
<?php
var_dump(extension_loaded('filter'));
var_dump(function_exists('filter_var'));
var_dump(defined('FILTER_THROW_ON_FAILURE'));
Ожидаемый результат
bool(true)
bool(true)
bool(true)

Последняя строка true для проверенного PHP 8.5.11, false для PHP 8.2–8.4. Эти результаты не являются обещанием того же окружения у читателя.

Правило на вызове не зависит от глобальной очисткиСамостоятельный пример
<?php
$value = '<mark>New</mark>';
echo filter_var($value, FILTER_UNSAFE_RAW), "\n";
echo htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
Ожидаемый результат
<mark>New</mark>
&lt;mark&gt;New&lt;/mark&gt;

Сохранение исходных данных и вывод в HTML заданы раздельно. Не нужно добиваться такой последовательности через filter.default для всех суперглобальных массивов.

Константы доступны при загруженном расширении. Все семь функций существуют с PHP 5.2; современная редакция примеров рассчитана на PHP 8.2+, отдельные новшества помечены. CALLBACK с массивными флагами требует 8.1.25/8.2.12+, THROW_ON_FAILURE — 8.5+. FULL_SPECIAL_CHARS зависит от default_charset; явный UTF-8 облегчает контроль кодировки.

Собрать ошибки заказа, затем передать проверенные значения

Для заказа обязательны email и количество 1…10. Учебная функция получает массив и возвращает отдельно errors и values. Она не выполняет SQL и не притворяется полным HTTP-контроллером: метод, CSRF, авторизация и сохранение разбираются в связанных главах.

Что происходит с данными
  1. Массив qty="2", email="bad"
  2. Проверка формы полей и диапазона
  3. В errors добавлен email
  4. Сохранение не запускается, пока errors не пуст
Проверка заказа без скрытых исправленийСамостоятельный пример
<?php
function validateOrder($input) {
    $errors = [];
    $values = [];
    if (!isset($input['qty']) || !is_string($input['qty'])) {
        $errors['qty'] = 'Введите количество';
    } else {
        $qty = filter_var($input['qty'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10]]);
        if ($qty === false) { $errors['qty'] = 'Нужно целое от 1 до 10'; }
        else { $values['qty'] = $qty; }
    }
    if (!isset($input['email']) || !is_string($input['email']) || filter_var($input['email'], FILTER_VALIDATE_EMAIL) === false) {
        $errors['email'] = 'Введите email';
    } else {
        $values['email'] = $input['email'];
    }
    return ['errors' => $errors, 'values' => $values];
}
$result = validateOrder(['qty' => '2', 'email' => 'bad']);
echo json_encode(array_keys($result['errors'])), "\n";
echo json_encode($result['values']), "\n";
Ожидаемый результат
["email"]
{"qty":2}

isset отсекает отсутствие/null, is_string — массив вместо поля; || прекращает проверку при первом true. В результате есть частично проверенные values, но они ещё не дают разрешение сохранять заказ: вызывающий код обязан проверить пустоту errors.

Упражнение: необязательный целый лимитСамостоятельный пример
<?php
function parseOptionalLimit($text) {
    if ($text === '') { return null; }
    return filter_var($text, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 100]]);
}
var_dump(parseOptionalLimit(''), parseOptionalLimit('0'), parseOptionalLimit('101'));
Ожидаемый результат
NULL
int(0)
bool(false)

Попробуйте сначала написать правило сами: пусто означает «лимит не задан», ноль допустим, диапазон до 100. Решение отдельно обрабатывает пустую строку до валидатора; NULL_ON_FAILURE здесь не нужен, потому что null уже имеет другой смысл.

Для десятков полей карта filter_var_array уменьшает повторение, но сообщения, обязательность и связи полей остаются частью вашей схемы. При большом импорте фильтрация происходит для каждого элемента; callback/PCRE и размер строк влияют на стоимость. Не фильтруйте один и тот же набор многократно ради каждого сообщения — сохраните результаты. Измеряйте на реальных данных. Продолжение: форма, CSRF и ответы, параметризованный SQL, контекстное кодирование вывода.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

filter_var_arrayПроверить несколько полей уже загруженного массива: например декодированный JSON заказа.

PHP 5.2+ · filter

filter_var_array(array $array, array|int $options = FILTER_DEFAULT, bool $add_empty = true): array|false|null
Параметр / результатДоговор
arrayИсходный массив не изменяется. Верхние ключи — имена полей.
optionsОдин ID фильтра для всех значений либо карта имя → ID / спецификация. В спецификации filter задаёт ID, flags — маску, options — настройки. Неуказанные в карте поля не копируются.
add_emptytrue добавляет отсутствующие имена карты со значением null; false исключает такие ключи. Это не правило обязательности и не заполнение пустой строки.
ВозвратМассив отфильтрованных значений; каждый неудачный валидатор обычно даёт false (либо null по флагу). Сигнатура допускает false/null; на успехе проверяйте поля, а не только сам массив.
Ошибки и ограниченияНекорректная конфигурация не является ошибкой пользовательского поля: возможны предупреждения, ValueError/TypeError. NULL_ON_FAILURE меняет отказ валидации, но отсутствие ключа с add_empty=true остаётся null.

Начните с обычного PHP-массива: JSON и HTTP для работы функции не нужны. В первом примере ключи — названия товаров, значения — текстовые остатки. Квадратные скобки создают массив, => связывает ключ со значением, $checked['Кисть'] читает одно поле результата. Во втором примере карта правил использует другие значения: имя поля слева, выбранный фильтр справа.

Путь данных в первом опыте
  1. Исходный массив содержит текстовые остатки "0" и "12"
  2. Один FILTER_VALIDATE_INT применяется к каждой записи
  3. Возвращён другой массив с числами 0 и 12
  4. Исходный массив остаётся текстовым
Применить одно правило к нескольким остаткамСамостоятельный пример
<?php
declare(strict_types=1);

$source = ['Кисть' => '0', 'Молоток' => '12'];
$checked = filter_var_array($source, FILTER_VALIDATE_INT);
var_dump($checked['Кисть'], $checked['Молоток']);
var_dump($source['Кисть']);
Ожидаемый результат
int(0)
int(12)
string(1) "0"

В новом массиве находятся числа, а исходное поле по-прежнему строка. var_dump показывает тип, который echo не отличил бы по внешнему виду. Для боевого импорта после вызова проверяйте каждый элемент: один неудачный фильтр не делает весь результат равным false.

Увидеть неверное поле, отсутствие и лишнее полеСамостоятельный пример
<?php
declare(strict_types=1);

$input = ['qty' => '2', 'email' => 'broken', 'extra' => 'ignored'];
$rules = [
    'qty' => FILTER_VALIDATE_INT,
    'email' => FILTER_VALIDATE_EMAIL,
    'note' => FILTER_UNSAFE_RAW,
];
var_export(filter_var_array($input, $rules));
echo "\n";
var_export(filter_var_array($input, $rules, false));
echo "\n";
Ожидаемый результат
array (
  'qty' => 2,
  'email' => false,
  'note' => NULL,
)
array (
  'qty' => 2,
  'email' => false,
)

var_export показывает массив в форме PHP-кода. qty превратился в число; email не прошёл правило и стал false; note отсутствовал и по умолчанию добавлен как null. Третий аргумент false запрещает добавлять отсутствующие ключи. Поле extra не описано в карте и не попало в результат ни в одном варианте. FILTER_UNSAFE_RAW у note не проверяет безопасность текста: это только перенос значения.

Собрать ошибки заказа и остановить дальнейшую обработкуСамостоятельный пример
<?php
declare(strict_types=1);

$input = ['qty' => '21', 'email' => 'not-email'];
$rules = [
    'qty' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 20]],
    'email' => FILTER_VALIDATE_EMAIL,
];
$values = filter_var_array($input, $rules);
if (!is_array($values)) {
    throw new RuntimeException('Не получены результаты проверки');
}
$errors = [];
if ($values['qty'] === false || $values['qty'] === null) {
    $errors[] = 'Количество: целое от 1 до 20';
}
if ($values['email'] === false || $values['email'] === null) {
    $errors[] = 'Email: укажите адрес';
}
if ($errors !== []) {
    echo "Заказ пока не принят:\n";
    foreach ($errors as $message) { echo '- ', $message, "\n"; }
} else {
    echo 'Проверенные поля: ', $values['qty'], ' шт.; ', $values['email'], "\n";
}
Ожидаемый результат
Заказ пока не принят:
- Количество: целое от 1 до 20
- Email: укажите адрес

Проверка из двух полей возвращает сразу две ошибки. $errors[] дописывает сообщение в список; пустой список означает, что обе проверки пройдены. При qty=2 и email=buyer@example.test успешная ветка напечатает эти значения. Это не создание заказа и не отправка письма: сохранение, права, CSRF и существование ящика проверяются отдельно. Для глубоко вложенных структур нужна отдельная схема, а не лишь эта карта верхних полей.

Импорт с ограничением количества и массивом метокСамостоятельный пример
<?php
$row = ['qty' => '6', 'ids' => ['12', 'wrong'], 'enabled' => 'no'];
$rules = [
    'qty' => ['filter' => FILTER_VALIDATE_INT, 'options' => ['min_range' => 1, 'max_range' => 5]],
    'ids' => ['filter' => FILTER_VALIDATE_INT, 'flags' => FILTER_REQUIRE_ARRAY],
    'enabled' => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
];
echo json_encode(filter_var_array($row, $rules)), "\n";
echo json_encode(filter_var_array(['a' => '1', 'b' => '2'], FILTER_VALIDATE_INT)), "\n";
Ожидаемый результат
{"qty":false,"ids":[12,false],"enabled":false}
{"a":1,"b":2}

false у qty — отказ, а false у enabled с NULL_ON_FAILURE — корректное «нет». Массив ids требует поэлементного контроля. Для вложенной бизнес-схемы нужны собственные явные проверки, не только верхняя карта.

Применение и границы. Один проход возвращает все результаты, но не транзакцию «всё или ничего». Соберите ошибки полей и лишь затем сохраняйте заказ. Декодирование и проверка вложенных данных.

Сравните: filter_var, filter_input_array. Карта выбора.

filter_idНайти ID фильтра по его имени в конфигурации; неизвестное имя обнаружить до обработки данных.

PHP 5.2+ · filter

filter_id(string $name): int|false
Параметр / результатДоговор
nameИмя из filter_list(), например int или validate_email. Это не имя константы FILTER_VALIDATE_INT и не INPUT_GET.
ВозвратЧисловой ID или false, если имя неизвестно. Имена регистрозависимы: int и INT различаются.
Ошибки и ограниченияСравнивайте с false строго. Не давайте пользовательскому полю выбирать произвольное правило: разрешённый список правил задаёт сервер.

Обычно достаточно написать FILTER_VALIDATE_INT прямо в коде. Имена вроде int полезны, когда разработчик хранит правила импортёра в конфигурации. filter_id связывает такое имя с числовым идентификатором, который понимает filter_var. Это не номер поля и не индекс массива.

Путь данных в первом опыте
  1. Настройка "int"
  2. filter_id ищет имя
  3. Возвращён ID FILTER_VALIDATE_INT
  4. "4" проверяется этим правилом
Получить идентификатор и применить егоСамостоятельный пример
<?php
declare(strict_types=1);

$id = filter_id('int');
echo 'ID int: ', $id, "\n";
echo 'Количество: ', filter_var('4', $id), "\n";
var_dump(filter_id('FILTER_VALIDATE_INT'));
Ожидаемый результат
ID int: 257
Количество: 4
bool(false)

257 — числовой идентификатор int в проверенной версии; в прикладном коде писать это число вместо понятной константы не требуется. Строка FILTER_VALIDATE_INT не является зарегистрированным именем фильтра, поэтому lookup возвращает false.

Применить разрешённые правила к колонкам импортаСамостоятельный пример
<?php
declare(strict_types=1);

$rules = ['stock' => 'int', 'email' => 'validate_email'];
$row = ['stock' => '8', 'email' => 'buyer@example.test'];
foreach ($rules as $column => $name) {
    $id = filter_id($name);
    if ($id === false) {
        throw new LogicException('Неизвестное правило: ' . $name);
    }
    $value = filter_var($row[$column], $id);
    echo $column, ': ', $value === false ? 'ошибка значения' : $value, "\n";
}
var_dump(filter_id('INT'));
Ожидаемый результат
stock: 8
email: buyer@example.test
bool(false)

Правила задаёт разработчик, а не посетитель формы. Ошибка имени правила останавливает работу как ошибка конфигурации; неверное значение относится к конкретному полю. Название INT не совпадает с int: регистр имеет значение.

Применение и границы. Числовые значения ID не нужно хранить в долгоживущем внешнем протоколе. Сохраняйте понятное имя и проверяйте его при чтении настроек.

Сравните: filter_list, filter_var. Карта выбора.

filter_listУвидеть доступные имена фильтров в текущей сборке PHP.

PHP 5.2+ · filter

filter_list(): array
Параметр / результатДоговор
ПараметрыНет. Список зависит от доступной версии расширения.
ВозвратМассив имён (строк); если фильтров нет — пустой массив. Числовой индекс элемента не равен ID фильтра.
Ошибки и ограниченияНаличие имени не означает, что фильтр рекомендован: исторический string остаётся устаревшим с PHP 8.1. Само расширение должно быть загружено.

Функция без аргументов возвращает список строк с именами. Его можно показать разработчику при установке импортёра или сопоставить с требуемыми возможностями. В этом примере foreach проходит список, а str_starts_with оставляет только имена с заданным началом. Короткие имена int, boolean и float тоже валидаторы, но не входят в эту выборку по префиксу.

Путь данных в первом опыте
  1. filter_list возвращает имена доступных фильтров
  2. Выбираем семейство validate_*
  3. Получаем видимые имена для диагностической страницы
  4. Наличие имени не заменяет проверку конкретного значения
Показать имена валидаторов, начинающиеся с validate_Самостоятельный пример
<?php
declare(strict_types=1);

foreach (filter_list() as $name) {
    if (str_starts_with($name, 'validate_')) {
        echo $name, "\n";
    }
}
Ожидаемый результат
validate_regexp
validate_domain
validate_url
validate_email
validate_ip
validate_mac

Это видимая часть реального списка PHP 8.5.11, а не проверка «что-то существует». Другие версии могут изменить набор и порядок. Для полного списка уберите условие if и оставьте echo внутри foreach.

Объяснить, готов ли импортёр к проверке своих полейСамостоятельный пример
<?php
declare(strict_types=1);

$available = filter_list();
$required = ['int' => 'остаток', 'validate_email' => 'email', 'phone_number' => 'телефон'];
foreach ($required as $name => $field) {
    if (in_array($name, $available, true)) {
        echo $field, ': можно использовать ', $name, "\n";
    } else {
        echo $field, ": встроенного фильтра с таким именем нет\n";
    }
}
Ожидаемый результат
остаток: можно использовать int
email: можно использовать validate_email
телефон: встроенного фильтра с таким именем нет

in_array ищет имя в списке; true включает строгое сравнение. Встроенного phone_number нет: для телефона нужно сначала определить договор страны/формата и выбрать соответствующую проверку или библиотеку. Молчаливый переход к FILTER_DEFAULT оставил бы значение без валидации.

Применение и границы. Если нужен один заранее известный фильтр, перечислять всё при каждом поле не требуется. Проверку окружения выполняют при установке или диагностике.

Сравните: filter_id, filter_var. Карта выбора.

filter_has_varПроверить, было ли имя поля в исходном запросе, независимо от его содержимого.

PHP 5.2+ · filter

filter_has_var(int $input_type, string $var_name): bool
Параметр / результатДоговор
input_typeINPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER или INPUT_ENV. Выбирает снимок входа SAPI, а не обычный массив.
var_nameТочное имя верхнего поля. Поле q= существует даже с пустым текстом.
Возвратtrue при наличии поля, false при отсутствии. Не сообщает, является ли значение строкой, непустым или допустимым.
Ошибки и ограниченияНеподдерживаемый тип источника — ValueError в современном PHP. Изменения $_GET/$_POST после начала запроса не меняют этот ответ.

Сохраните полный пример как example.php в пустой учебной папке, запустите в ней php -S 127.0.0.1:8080 и откройте http://127.0.0.1:8080/example.php. Нажмите кнопку с пустым поисковым полем и сравните результат с первым открытием страницы. action="example.php" возвращает форму этому же файлу, method выбирает способ передачи, name становится именем поля. Подпись label помогает человеку, но ключом PHP служит именно name. Подробно о связи HTML-формы с PHP.

Путь данных в первом опыте
  1. Первый GET не содержит поля q
  2. Форма отправляет name=q, даже если текст пуст
  3. URL после отправки заканчивается ?q=
  4. filter_has_var различает отсутствие и присутствие; содержимое проверяется отдельно
Пустое поле формы и отсутствующее поле — разные ситуацииПример для указанного окружения
<?php
header('Content-Type: text/html; charset=UTF-8');
?>
<!doctype html>
<meta charset="utf-8">
<title>Наличие поля</title>
<form action="example.php" method="get">
<label>Поиск <input name="q" value=""></label>
<button>Проверить поле</button>
</form>
<?php
if (filter_has_var(INPUT_GET, 'q')) {
    echo '<p>Поле q пришло. Теперь можно проверить его содержимое.</p>';
} else {
    echo '<p>Поля q в запросе ещё нет. Отправьте форму.</p>';
}
Ожидаемый результат
<!doctype html>
<meta charset="utf-8">
<title>Наличие поля</title>
<form action="example.php" method="get">
<label>Поиск <input name="q" value=""></label>
<button>Проверить поле</button>
</form>
<p>Поле q пришло. Теперь можно проверить его содержимое.</p>

Результат показан после нажатия кнопки при пустом поле, то есть для example.php?q=. При первом открытии без query отображается другое сообщение. INPUT_GET выбирает поля URL. Проверка отвечает только на вопрос о наличии: q[]=что-то тоже означает присутствие поля, но уже другой формы данных. Непустоту и скалярный тип отдельно проверяйте перед поиском. Код не отражает пользовательский текст и ничего не сохраняет.

Видимый результат в браузере; для отправки откройте скачанный пример

Поле q пришло. Теперь можно проверить его содержимое.

Присваивание не добавляет поле в исходный запросПример для указанного окружения
<?php
header('Content-Type: text/plain; charset=UTF-8');
$_GET['added'] = 'local';
unset($_GET['sent']);
var_dump(filter_has_var(INPUT_GET, 'added'));
var_dump(filter_has_var(INPUT_GET, 'sent'));
var_dump(isset($_GET['added']), isset($_GET['sent']));
Ожидаемый результат
bool(false)
bool(true)
bool(true)
bool(false)

Откройте /example.php?sent=wire. Сначала SAPI сохранил sent, затем код изменил только массив. isset здесь показывает его текущее состояние. Это самостоятельный HTTP-обработчик, не CLI-имитация. Сохраните код как example.php, в его каталоге запустите php -S 127.0.0.1:8080 и откройте указанный адрес. Встроенный сервер используется для локального опыта. header выбирает текстовый ответ до вывода. GET-поля находятся после ?; & разделяет поля. Как проходит HTTP-запрос.

Применение и границы. При тестировании чистой функции передавайте массив и используйте array_key_exists/isset согласно нужному договору. Подстановка $_POST в CLI не создаёт HTTP-запрос. Источники и SAPI.

Сравните: filter_input, filter_input_array. Карта выбора.

Filter\FilterExceptionОбщий тип исключений расширения Filter, который можно перехватить на границе импортёра.

PHP 8.5+ (THROW_ON_FAILURE; проверено 8.5.11) · filter

class Filter\FilterException extends Exception
Параметр / результатДоговор
Собственные параметрыУ класса нет собственного конструктора: наследуется Exception::__construct(message, code, previous).
ИерархияНаследник Exception; FilterFailedException — его потомок. Реализует Throwable через Exception.
ВозвратСам класс не запускает фильтрацию. Его экземпляр — объект ошибки; методы позволяют читать сообщение, код и причину.
Ошибки и ограниченияОбщий catch (Filter\FilterException) не ловит посторонний ValueError ошибочной настройки. Классы проверены в PHP 8.5.11; официальный флаг THROW_ON_FAILURE доступен с PHP 8.5.

try выполняет попытку; catch (Тип $error) получает объект сбоя указанного типа. -> вызывает метод объекта; throw передаёт исключение обработчику. Начало работы с исключениями.

Это имена классов в пространстве имён Filter; обратный слеш отделяет пространство от класса. new создаёт объект исключения, instanceof проверяет принадлежность типу. Собственных публичных методов и полей эти классы не добавляют. Пространства имён.

Унаследованная часть ExceptionГде разобрана
__construct(string $message = "", int $code = 0, ?Throwable $previous = null)Конструктор Exception
message, code, file, line; previous и trace в состоянии исключенияПоля и происхождение данных
getMessage/getCode/getFile/getLine/getTrace/getTraceAsString/getPrevious/__toString; закрытый __cloneПолные договоры методов Exception
Путь данных в первом опыте
  1. Неверное количество "bad"
  2. INT + THROW_ON_FAILURE
  3. Создан FilterFailedException
  4. catch FilterException ловит дочерний тип
Общий перехват ошибки фильтраСамостоятельный пример
<?php
try {
    filter_var('bad', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
} catch (Filter\FilterException $error) {
    echo "Количество не прошло проверку\n";
    var_dump($error instanceof Exception);
}
Ожидаемый результат
Количество не прошло проверку
bool(true)

PHP 8.5+. Посетитель получает понятное сообщение поля, а не внутреннее сообщение со значением входа.

Сохранить причину при переводе ошибки в свою задачуСамостоятельный пример
<?php
try {
    try {
        filter_var('wrong', FILTER_VALIDATE_EMAIL, FILTER_THROW_ON_FAILURE);
    } catch (Filter\FilterException $cause) {
        throw new RuntimeException('Импорт: неверный email', 0, $cause);
    }
} catch (RuntimeException $error) {
    echo $error->getMessage(), "\n";
    echo get_class($error->getPrevious()), "\n";
}
Ожидаемый результат
Импорт: неверный email
Filter\FilterFailedException

Внутренний catch добавляет контекст задачи и сохраняет объект причины третьим аргументом. Это контролируемый импорт, не требование оборачивать каждое поле формы.

Применение и границы. У новой страницы класса PHP.net заголовок не содержит отдельного version marker; здесь не выводится точная дата класса из такого заголовка. Практическое использование с THROW_ON_FAILURE требует PHP 8.5+. Сообщение фильтра может содержать исходное значение: не выводите его пользователю и не складывайте секреты в журнал без политики редактирования.

Сравните: Filter\FilterFailedException, filter_var. Карта выбора.

Filter\FilterFailedExceptionОтказ валидатора становится исключением только при явно выбранном THROW_ON_FAILURE.

PHP 8.5+ (THROW_ON_FAILURE; проверено 8.5.11) · filter

class Filter\FilterFailedException extends Filter\FilterException
Параметр / результатДоговор
ПричинаВыбран FILTER_THROW_ON_FAILURE и значение не прошло FILTER_VALIDATE_*.
Поля/методыСобственных не добавляет; унаследован конструктор и API Exception.
Не путатьfalse от пользовательского FILTER_CALLBACK остаётся его результатом. ValueError конфигурации — иной класс.
ВозвратЭто тип объекта исключения, а не новое возвращаемое значение функции. Допустимый вход продолжает возвращать обычные данные.
Ошибки и ограниченияNULL_ON_FAILURE и THROW_ON_FAILURE несовместимы; default не подавляет THROW_ON_FAILURE. Для обычной формы коллекция ошибок полей часто удобнее исключений.

try выполняет попытку; catch (Тип $error) получает объект сбоя указанного типа. -> вызывает метод объекта; throw передаёт исключение обработчику. Начало работы с исключениями.

Это имена классов в пространстве имён Filter; обратный слеш отделяет пространство от класса. new создаёт объект исключения, instanceof проверяет принадлежность типу. Собственных публичных методов и полей эти классы не добавляют. Пространства имён.

Унаследованная часть ExceptionГде разобрана
__construct(string $message = "", int $code = 0, ?Throwable $previous = null)Конструктор Exception
message, code, file, line; previous и trace в состоянии исключенияПоля и происхождение данных
getMessage/getCode/getFile/getLine/getTrace/getTraceAsString/getPrevious/__toString; закрытый __cloneПолные договоры методов Exception
Путь данных в первом опыте
  1. "4" проходит INT
  2. Получаем int(4)
  3. "x" не проходит INT + THROW
  4. catch FilterFailedException получает сбой
Успешное значение и отказ в одном договореСамостоятельный пример
<?php
var_dump(filter_var('4', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE));
try {
    filter_var('x', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE);
} catch (Filter\FilterFailedException $error) {
    echo "Нужно целое количество\n";
}
Ожидаемый результат
int(4)
Нужно целое количество

PHP 8.5+. Исключение не выдаётся при каждом вызове: это выбранная политика только неуспеха.

default не отменяет выбрасываниеСамостоятельный пример
<?php
try {
    filter_var('x', FILTER_VALIDATE_INT, [
        'flags' => FILTER_THROW_ON_FAILURE,
        'options' => ['default' => 7],
    ]);
} catch (Filter\FilterFailedException $error) {
    echo "Исключение, а не значение 7\n";
}
try {
    filter_var('x', FILTER_VALIDATE_INT, FILTER_THROW_ON_FAILURE | FILTER_NULL_ON_FAILURE);
} catch (ValueError $error) {
    echo "Несовместимые настройки отказа\n";
}
Ожидаемый результат
Исключение, а не значение 7
Несовместимые настройки отказа

Не смешивайте способы сообщить о неуспехе. Во второй попытке ошиблась конфигурация программы ещё до полезной проверки поля.

Применение и границы. Не нужно анализировать текст исключения регулярным выражением для определения поля: обрабатывайте вызов в известном контексте либо храните структурированную карту ошибок. Для совместимости с PHP 8.2–8.4 используйте false/null и собственную явную ветвь обработки.

Сравните: Filter\FilterException, filter_var. Карта выбора.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.