xml_set_external_entity_ref_handler()
Функция xml_set_external_entity_ref_handler() регистрирует пользовательский обработчик внешних ссылок на сущности в SAX-парсере PHP.
Функция xml_set_external_entity_ref_handler() — встроенная функция PHP, которая регистрирует пользовательский обратный вызов для обработки внешних ссылок на сущности в устаревшем SAX (Expat) XML-парсере. Внешняя сущность — это ссылка внутри XML-документа, объявленная через <!ENTITY name SYSTEM "uri">, которая указывает на содержимое, хранящееся вне документа. Когда парсер встречает такую ссылку в процессе разбора, он вызывает ваш обратный вызов, чтобы вы могли решить, что с ней делать: проигнорировать, загрузить одобренные данные из базы данных или отклонить как часть проверки безопасности.
На этой странице рассматриваются синтаксис функции, параметры, которые PHP передаёт вашему обратному вызову, возвращаемое значение, полный рабочий пример, а также предостережения по безопасности и об устаревании, которые необходимо знать перед использованием.
Синтаксис
xml_set_external_entity_ref_handler(XMLParser $parser, callable $handler): boolПараметры
| Параметр | Описание |
|---|---|
$parser | Ресурс XML-парсера, созданный с помощью xml_parser_create(). Обработчик привязывается к данному конкретному парсеру. |
$handler | Обратный вызов, выполняемый при каждой внешней ссылке на сущность. Может быть именем функции (в виде строки) или — если использовался xml_set_object() — именем метода. Передача пустой строки отменяет регистрацию обработчика. |
Возвращаемое значение
Возвращает true при успехе или false при ошибке (например, если $parser не является допустимым парсером).
Сигнатура обратного вызова
PHP вызывает ваш обработчик с пятью аргументами в следующем порядке:
handler(XMLParser $parser, string $open_entity_names, string $base, string $system_id, ?string $public_id): int$open_entity_names— разделённый пробелами список сущностей, открытых в данный момент; используется для обнаружения рекурсии.$base— базовый URI для разрешения$system_id(обычно пустая строка).$system_id— системный идентификатор (URI изSYSTEM "...") внешней сущности.$public_id— публичный идентификатор илиnull, если он не был объявлен.
Ваш обратный вызов должен возвращать ненулевое (истинное) значение, чтобы парсинг продолжился. Возврат 0, false или отсутствие возвращаемого значения прерывает разбор с ошибкой XML_ERROR_EXTERNAL_ENTITY_HANDLING.
Примеры использования
Рассмотрим практический пример использования xml_set_external_entity_ref_handler() в PHP.
Пример: установка функции-обработчика внешних ссылок на сущности
Предположим, у вас есть XML-документ, который ссылается на внешнюю сущность, и вы хотите проверить эту ссылку в процессе разбора. Вы создаёте парсер с помощью xml_parser_create(), регистрируете обработчик, разбираете данные с помощью xml_parse() и освобождаете парсер с помощью xml_parser_free():
Установка функции-обработчика внешних ссылок на сущности в PHP
function handle_external_entity_ref($parser, $open_entity_names, $base, $system_id, $public_id) {
// Inspect — but do NOT blindly load — the external entity.
echo "External entity referenced: {$system_id}\n";
// Return a non-zero value so parsing continues.
return 1;
}
$xml_parser = xml_parser_create();
xml_set_external_entity_ref_handler($xml_parser, "handle_external_entity_ref");
$xml_data = '<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY ext SYSTEM "data.xml">
]>
<root>&ext;</root>';
xml_parse($xml_parser, $xml_data, true);
xml_parser_free($xml_parser);Обработчик срабатывает один раз для ссылки &ext; и выводит:
External entity referenced: data.xmlПоскольку обратный вызов только выводит системный идентификатор и возвращает 1, парсеру даётся команда продолжить работу без фактической загрузки data.xml — что является именно тем безопасным поведением по умолчанию, которое вам нужно.
Почему обработчик может никогда не сработать
В большинстве современных сборок PHP загрузка внешних сущностей отключена на уровне libxml, поэтому парсер молча игнорирует ссылки на сущности, и ваш обратный вызов никогда не вызывается. Это намеренное ужесточение безопасности. Если вам необходимо включить её, управление осуществляется глобально с помощью libxml_disable_entity_loader() — однако для чего-либо кроме доверенных и контролируемых входных данных загрузку следует оставить отключённой.
⚠️ Предупреждение о безопасности: Обработка внешних сущностей — классический вектор для атак XML External Entity (XXE), которые могут раскрыть локальные файлы (
file:///etc/passwd), инициировать запросы на стороне сервера или вызвать отказ в обслуживании. Никогда не разрешайте$system_idдля загрузки произвольных URI или локальных путей из ненадёжных входных данных. Для разбора, чувствительного к безопасности, предпочтительнее использовать современные библиотеки, такие какDOMDocumentилиXMLReader, с отключённой загрузкой сущностей.
Примечание об устаревании: Начиная с PHP 8.4, передача некаллабельной строки в качестве обработчика является устаревшей — простое имя функции, которая существует, по-прежнему работает, но неразрешённая строка теперь вызывает уведомление об устаревании. Для прямой совместимости передавайте настоящий callable, например
Closureили массив[$object, 'method']. Устаревшее расширение Expat SAX в целом находится в режиме обслуживания — для нового кода рекомендуется использоватьXMLReaderилиDOMDocument.
Заключение
В этой статье мы рассмотрели функцию PHP xml_set_external_entity_ref_handler(): её синтаксис, пять аргументов, которые PHP передаёт вашему обратному вызову, почему обратный вызов должен возвращать ненулевое значение для продолжения разбора, и полный рабочий пример. Мы также указали на две вещи, которые вызывают затруднения: обработчик часто никогда не срабатывает, поскольку загрузка внешних сущностей по умолчанию отключена, а передача ненадёжных входных данных через него открывает возможность для XXE-атак. Используйте его только с доверенными данными, предпочитайте настоящий callable вместо строкового имени на PHP 8.4+, и для чего-либо чувствительного к безопасности обращайтесь к XMLReader или DOMDocument.
Для получения информации о связанных SAX-обработчиках смотрите xml_set_element_handler() и xml_set_object().