PHP Libxml
libxml error collection, options and version info.
Key PointsBy default XML warnings print to the page — collect them with `libxml_use_internal_errors`.
Functions
| Function | Purpose |
|---|---|
| libxml_use_internal_errors | 开启/关闭内部错误收集 |
| libxml_get_errors | 取错误列表 |
| libxml_get_last_error | 取最后一个错误 |
| libxml_clear_errors | 清空错误缓冲 |
| libxml_set_streams_context | 设置流上下文(超时、代理) |
| libxml_disable_entity_loader | 禁用外部实体(8.0 起弃用) |
Collecting errors
PHP
<?php
libxml_use_internal_errors(true);
$bad = "<root><a>1</b></root>";
$doc = new DOMDocument();
$ok = $doc->loadXML($bad);
if (!$ok) {
foreach (libxml_get_errors() as $e) {
printf("级别 %d 第 %d 行: %s", $e->level, $e->line, trim($e->message) . "\n");
}
libxml_clear_errors();
}
echo $ok ? "解析成功" : "解析失败", "\n";Output
级别 2 第 1 行: Opening and ending tag mismatch: a line 1 and root
解析失败$e->level is 1 warning, 2 error, 3 fatal. line and column locate the issue; message describes it.
Forgetting
libxml_clear_errors() lets errors accumulate across parses in one request — the next parse reports stale ones.Options
PHP
<?php
$opts = LIBXML_NONET | LIBXML_NOBLANKS | LIBXML_NOCDATA;
$doc = new DOMDocument();
$doc->loadXML('<r><a>1</a> <b><![CDATA[x]]></b></r>', $opts);
echo $doc->getElementsByTagName("b")->item(0)->nodeValue, "\n";
// 关闭空白保留后,元素之间的空白文本节点被去掉
echo $doc->documentElement->childNodes->length, "\n";Output
x
2| Option | Effect |
|---|---|
| LIBXML_NONET | 禁止网络加载(防 XXE) |
| LIBXML_NOBLANKS | 去掉无意义空白节点 |
| LIBXML_NOCDATA | CDATA 合并为文本 |
| LIBXML_NOENT | 展开实体(危险) |
| LIBXML_DTDLOAD | 加载外部 DTD(危险) |
LIBXML_NOENT and LIBXML_DTDLOAD are the entry points for XXE. Never use them unless you fully understand the risk.Timeouts and context
PHP
<?php
$ctx = stream_context_create([
"http" => [
"timeout" => 5,
"header" => "User-Agent: MyApp/1.0\r\n",
],
]);
libxml_set_streams_context($ctx);
$doc = new DOMDocument();
$ok = @$doc->load("https://example.com/feed.xml");
echo $ok ? "加载成功" : "加载失败(超时或不可达)", "\n";Output
加载成功DOMDocument::load has no default timeout on remote URLs and will hang. **Set timeout via a stream context** or one slow site blocks a worker.
Version info
PHP
<?php
echo LIBXML_DOTTED_VERSION, "\n";
echo extension_loaded("libxml") ? "libxml 已加载" : "未加载", "\n";
echo function_exists("libxml_use_internal_errors") ? "函数可用" : "函数缺失", "\n";Output
2.9.14
libxml 已加载
函数可用函数
| 函数 | 作用 |
|---|---|
| libxml_use_internal_errors | 开启/关闭内部错误收集 |
| libxml_get_errors | 取错误列表 |
| libxml_get_last_error | 取最后一个错误 |
| libxml_clear_errors | 清空错误缓冲 |
| libxml_set_streams_context | 设置流上下文(超时、代理) |
| libxml_disable_entity_loader | 禁用外部实体(8.0 起弃用) |
收集错误
PHP
<?php
libxml_use_internal_errors(true);
$bad = "<root><a>1</b></root>";
$doc = new DOMDocument();
$ok = $doc->loadXML($bad);
if (!$ok) {
foreach (libxml_get_errors() as $e) {
printf("级别 %d 第 %d 行: %s", $e->level, $e->line, trim($e->message) . "\n");
}
libxml_clear_errors();
}
echo $ok ? "解析成功" : "解析失败", "\n";Output
级别 2 第 1 行: Opening and ending tag mismatch: a line 1 and root
解析失败$e->level 是 1 警告、2 错误、3 致命。$e->line、$e->column 定位问题位置,$e->message 是英文描述。
**忘记
libxml_clear_errors()** 会让错误在请求内累积,下一次解析拿到的是上次的残留。每次解析前或处理后都要清。选项
PHP
<?php
$opts = LIBXML_NONET | LIBXML_NOBLANKS | LIBXML_NOCDATA;
$doc = new DOMDocument();
$doc->loadXML('<r><a>1</a> <b><![CDATA[x]]></b></r>', $opts);
echo $doc->getElementsByTagName("b")->item(0)->nodeValue, "\n";
// 关闭空白保留后,元素之间的空白文本节点被去掉
echo $doc->documentElement->childNodes->length, "\n";Output
x
2| 选项 | 作用 |
|---|---|
| LIBXML_NONET | 禁止网络加载(防 XXE) |
| LIBXML_NOBLANKS | 去掉无意义空白节点 |
| LIBXML_NOCDATA | CDATA 合并为文本 |
| LIBXML_NOENT | 展开实体(危险) |
| LIBXML_DTDLOAD | 加载外部 DTD(危险) |
LIBXML_NOENT 和 LIBXML_DTDLOAD 是 XXE 攻击的入口。除非明确知道自己在做什么,否则永远不要加这两个选项。超时与上下文
PHP
<?php
$ctx = stream_context_create([
"http" => [
"timeout" => 5,
"header" => "User-Agent: MyApp/1.0\r\n",
],
]);
libxml_set_streams_context($ctx);
$doc = new DOMDocument();
$ok = @$doc->load("https://example.com/feed.xml");
echo $ok ? "加载成功" : "加载失败(超时或不可达)", "\n";Output
加载成功DOMDocument::load 直接读远程 URL 时默认没有超时,会一直等。**必须通过流上下文设 timeout**,否则一个慢站点能拖住整个 PHP-FPM worker。
版本信息
PHP
<?php
echo LIBXML_DOTTED_VERSION, "\n";
echo extension_loaded("libxml") ? "libxml 已加载" : "未加载", "\n";
echo function_exists("libxml_use_internal_errors") ? "函数可用" : "函数缺失", "\n";Output
2.9.14
libxml 已加载
函数可用