Глава 82 · Практика
Фильтрация данных: полный справочник Filter
Все семь функций, валидаторы, санитайзеры, флаги, ошибки, HTTP-вход и практика без потери исходных данных.
Сначала задача: принять количество, не подменив ввод
Из текста поля в проверенное количество
У формы qty приходит строкой. Проверка отвечает, можно ли использовать её как целое число в заданном диапазоне.
- qty = строка «2»
Сначала исключаем массив вместо одного поля.
- FILTER_VALIDATE_INT
Правило: целое число от 1 до 10.
- Результат: int 2
Сохраняем проверенное число отдельно от исходного текста.
- «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 ...; } объявляет повторяемое действие с локальным параметром. Массивы и ключи; ветвление и обход.
- Человек ввёл "3"
- filter_var с FILTER_VALIDATE_INT
- Результат int(3) присвоен $quantity
- Приложение продолжает работу с количеством
<?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 принимает переданное значение. Это две независимые оси выбора.
| Задача | Точка входа |
|---|---|
| Одна переменная / поле уже декодированного JSON | filter_var |
| Группа полей массива | filter_var_array |
| Одно исходное поле запроса | filter_input |
| Карта исходных полей | filter_input_array |
| Проверить факт наличия исходного имени | filter_has_var |
| Имена и их ID | filter_list + filter_id |
Результат, отказ, отсутствие, 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; это ещё не массив результатов. |
- "bad" не проходит INT
- Обычный режим → false
- Опция default=5 в другом вызове → 5
- Подстановка скрывает причину: решите, допустимо ли это
<?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 не перечитывают ваши присваивания.
- CLI не получил HTTP query
- Код присвоил $_GET["qty"]="8"
- filter_var видит текущее значение 8
- filter_input в этом CLI не получает поле qty
<?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 не становится большим точным числом.
| Правило | Смысл |
|---|---|
| Параметры options | default — замена обычного отказа; min_range и max_range — целочисленные границы. |
| Флаги | FILTER_FLAG_ALLOW_OCTAL, FILTER_FLAG_ALLOW_HEX; общие структурные флаги и политика отказа. |
| Результат | int; false/null/исключение в зависимости от политики. Для идентификатора больше PHP_INT_MAX сохраните проверенную строку, не float. |
- Текст " 12 "
- Краевые пробелы убраны; десятичная запись проверена
- Границы 0…50 соблюдены
- Возвращён 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 позволяет отдельно представить третье состояние — отказ.
| Правило | Смысл |
|---|---|
| options | default — результат при обычном отказе. |
| Результат | true или false; при отказе без NULL_ON_FAILURE тоже false. |
| Форма checkbox | Неотмеченный checkbox часто вообще не отправляется. Сначала различите отсутствие, затем примените правило приложения. |
- Настройка "off"
- Булевый фильтр распознаёт отключённое состояние
- Возвращает false
- При "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» имеет согласованный смысл выключения. Пример печатает решение; самих уведомлений он не отправляет.
<?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 после проверки формата и обрезания краевых пробелов. Десятичный разделитель задаётся явно, не угадывается по стране пользователя.
- Текст "12,75"
- decimal = ","
- Проверка формата
- 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"] и при совпадении возвращает исходную строку. Чтобы проверить всю запись, шаблон должен сам задавать её границы.
- "WH-42"
- Шаблон требует WH- и ровно две цифры
- Вся строка совпала
- Возвращена "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.
- "https://example.org/report"
- Проверка URL с PATH_REQUIRED
- Синтаксис и наличие пути подходят
- Строка возвращена; сетевого запроса не было
<?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-часть. Это не проверка существования страницы.
<?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&view=full">Сайт автора</a>Здесь URL нужен как ссылка в HTML, сервер сам по нему не обращается. Валидатор проверяет запись, parse_url выделяет схему, а htmlspecialchars защищает границы кавычечного HTML-атрибута. Амперсанд из query записан как & в исходнике страницы; браузер передаст обычный & при переходе. Домен example.test учебный, его существование не проверялось. Если по пользовательскому адресу должен обращаться сам сервер, нужен отдельный сетевой договор и защита от SSRF.
<?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: запросы здесь не выполняются.
<?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.
- "smtp.example.org"
- DOMAIN + HOSTNAME проверяет метки
- Длины и символы подходят
- Строка принята без запроса DNS
<?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 нужно выбирать по назначению поля.
<?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 и учётные данные проверяются на следующей ступени.
<?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 и возвращает строку; он не отправляет письмо и не подтверждает владение ящиком. Комментарии, переносы и домен без точки не входят в поддерживаемую форму.
- "team+orders@example.org"
- Проверяется синтаксис адреса
- Возвращена та же строка
- Подтверждение ящика — следующий отдельный процесс
<?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. Не удаляем случайные символы и не отправляем письмо «исправленному» получателю. Подтверждение адреса требует отдельного ограниченного по времени кода или ссылки.
<?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. Ограничения диапазона выбираются явно; синтаксически верный адрес может принадлежать внутренней сети.
- "192.168.1.5"
- IPV4 подтверждает форму
- NO_PRIV_RANGE запрещает частный диапазон
- Результат 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 доверять нельзя без настройки известных прокси.
<?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 проверяет формат, но не подтверждает существование адаптера или уникальность оборудования. Результат — исходная строка, не каноническая запись.
- "02:11:22:33:44:55"
- Проверка формы 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 | Нулевая маска; никаких дополнительных флагов. |
- Текст "A" + перевод строки + "B"
- RAW без флагов сохраняет строку
- RAW + STRIP_LOW удаляет байт 10
- Получаем "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&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.
- Исходный текст <b>Sale</b>
- Кодирование для HTML-текста
- В ответе <b>Sale</b>
- Браузер показывает угловые скобки как текст
<?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";
Ожидаемый результат
"<b>"
"<b>"
"<b>"
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><b>Кисть</b> & валик</p>В PHP-выводе появились сущности < и &. Браузер прочитает их как видимые символы < и &, а не начнёт выполнять теги из комментария. Ниже показан именно такой результат. 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 удаляет символы вне разрешённого набора. Это полезно лишь при явно согласованной операции редактирования текста; для адреса получателя незаметное исправление опасно: человек отправил один текст, а приложение создало другой адрес.
- Ввод "a b@example.org"
- SANITIZE_EMAIL удаляет пробел
- Получено "ab@example.org"
- Адрес изменился; исходный ввод лучше отклонить
<?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. Первый не выбирает безопасный протокол, второй не предназначен для кодирования целого адреса с его разделителями.
- Поисковый текст "red shoes"
- SANITIZE_ENCODED кодирует пробел
- "red%20shoes" — компонент запроса
- Добавляем после ?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 без флагов делает то же самое, удаляя десятичную точку.
- "12.50"
- NUMBER_FLOAT без ALLOW_FRACTION
- Точка удалена
- "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.
- Имя "O'Neil"
- ADD_SLASHES ставит \ перед апострофом
- "O\'Neil"
- Для 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. Старую строку нельзя механически заменить одной «очисткой на все случаи»: сначала найдите, что приложение хотело получить.
- В базе хранится "<b>New</b>"
- Требование: показать как текст HTML
- htmlspecialchars кодирует на выходе
- Исходная строка остаётся пригодной для других представлений
<?php
$original = '<b>New</b>';
echo htmlspecialchars($original, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
echo $original, "\n";
Ожидаемый результат
<b>New</b>
<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, а не массив настроек обычного валидатора.
- " box "
- filter_var вызывает normalizeSku(" box ")
- trim → strtoupper → проверка разрешённых кодов
- 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.
<?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_flags | INI_PERDIR; таблица INI PHP.net указывает default NULL, текст уточняет исходную совместимую настройку FILTER_FLAG_NO_ENCODE_QUOTES. Для legacy full_special_chars устанавливают 0, если нужно кодирование кавычек. Это не рекомендация глобальной очистки. |
| Где менять INI_PERDIR | php.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. |
- Текущий процесс PHP
- extension_loaded("filter")
- Расширение доступно
- Можно проверять нужное правило и его версию
<?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>
<mark>New</mark>
Сохранение исходных данных и вывод в 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, авторизация и сохранение разбираются в связанных главах.
- Массив qty="2", email="bad"
- Проверка формы полей и диапазона
- В errors добавлен email
- Сохранение не запускается, пока 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 заказа.
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_empty | true добавляет отсутствующие имена карты со значением null; false исключает такие ключи. Это не правило обязательности и не заполнение пустой строки. |
| Возврат | Массив отфильтрованных значений; каждый неудачный валидатор обычно даёт false (либо null по флагу). Сигнатура допускает false/null; на успехе проверяйте поля, а не только сам массив. |
| Ошибки и ограничения | Некорректная конфигурация не является ошибкой пользовательского поля: возможны предупреждения, ValueError/TypeError. NULL_ON_FAILURE меняет отказ валидации, но отсутствие ключа с add_empty=true остаётся null. |
Начните с обычного PHP-массива: JSON и HTTP для работы функции не нужны. В первом примере ключи — названия товаров, значения — текстовые остатки. Квадратные скобки создают массив, => связывает ключ со значением, $checked['Кисть'] читает одно поле результата. Во втором примере карта правил использует другие значения: имя поля слева, выбранный фильтр справа.
- Исходный массив содержит текстовые остатки "0" и "12"
- Один FILTER_VALIDATE_INT применяется к каждой записи
- Возвращён другой массив с числами 0 и 12
- Исходный массив остаётся текстовым
<?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 фильтра по его имени в конфигурации; неизвестное имя обнаружить до обработки данных.
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. Это не номер поля и не индекс массива.
- Настройка "int"
- filter_id ищет имя
- Возвращён ID FILTER_VALIDATE_INT
- "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.
filter_list(): array| Параметр / результат | Договор |
|---|---|
| Параметры | Нет. Список зависит от доступной версии расширения. |
| Возврат | Массив имён (строк); если фильтров нет — пустой массив. Числовой индекс элемента не равен ID фильтра. |
| Ошибки и ограничения | Наличие имени не означает, что фильтр рекомендован: исторический string остаётся устаревшим с PHP 8.1. Само расширение должно быть загружено. |
Функция без аргументов возвращает список строк с именами. Его можно показать разработчику при установке импортёра или сопоставить с требуемыми возможностями. В этом примере foreach проходит список, а str_starts_with оставляет только имена с заданным началом. Короткие имена int, boolean и float тоже валидаторы, но не входят в эту выборку по префиксу.
- filter_list возвращает имена доступных фильтров
- Выбираем семейство 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Проверить, было ли имя поля в исходном запросе, независимо от его содержимого.
filter_has_var(int $input_type, string $var_name): bool| Параметр / результат | Договор |
|---|---|
| input_type | INPUT_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.
- Первый GET не содержит поля q
- Форма отправляет name=q, даже если текст пуст
- URL после отправки заканчивается ?q=
- 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, который можно перехватить на границе импортёра.
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 |
- Неверное количество "bad"
- INT + THROW_ON_FAILURE
- Создан FilterFailedException
- 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.
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 |
- "4" проходит INT
- Получаем int(4)
- "x" не проходит INT + THROW
- 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+. Исключение не выдаётся при каждом вызове: это выбранная политика только неуспеха.
<?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. Карта выбора.