DOMDocument::schemaValidate

(PHP 5, PHP 7, PHP 8)

DOMDocument::schemaValidate 基于模式验证文档。仅支持 XML Schema 1.0。

描述

public DOMDocument::schemaValidate(string $filename, int $flags = 0): bool

基于给定的模式文件验证文档。

参数

filename

模式的路径。

flags

Libxml 模式验证标志的位掩码。目前唯一支持的值是 LIBXML_SCHEMA_CREATE。自 Libxml 2.6.14 起可用。

返回值

如果成功,则返回 true,如果失败,则返回 false

参见

添加笔记

用户贡献的笔记 6 笔记

38
Mike A.
18 年前
要从 DOMDocument::schemaValidate 获取更详细的反馈,请禁用 libxml 错误并自行获取错误信息。有关更多信息,请参见 https://php.net/manual/en/ref.libxml.php

example.xml
<?xml version="1.0"?>
<example>
<child_string>这是一个示例。</child_string>
<child_integer>错误条件。</child_integer>
</example>

example.xsd
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:element name="example">
<xs:complexType>
<xs:sequence>
<xs:element name="child_string" type="xs:string"/>
<xs:element name="child_integer" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

<?php

function libxml_display_error($error)
{
$return = "<br/>\n";
switch (
$error->level) {
case
LIBXML_ERR_WARNING:
$return .= "<b>Warning $error->code</b>: ";
break;
case
LIBXML_ERR_ERROR:
$return .= "<b>Error $error->code</b>: ";
break;
case
LIBXML_ERR_FATAL:
$return .= "<b>Fatal Error $error->code</b>: ";
break;
}
$return .= trim($error->message);
if (
$error->file) {
$return .= " in <b>$error->file</b>";
}
$return .= " on line <b>$error->line</b>\n";

return
$return;
}

function
libxml_display_errors() {
$errors = libxml_get_errors();
foreach (
$errors as $error) {
print
libxml_display_error($error);
}
libxml_clear_errors();
}

// 启用用户错误处理
libxml_use_internal_errors(true);

$xml = new DOMDocument();
$xml->load('example.xml');

if (!
$xml->schemaValidate('example.xsd')) {
print
'<b>DOMDocument::schemaValidate() 生成的错误!</b>';
libxml_display_errors();
}

?>

旧错误消息
警告:DOMDocument::schemaValidate() [function.schemaValidate]:元素“child_integer”:“错误条件。”不是原子类型“xs:integer”的有效值。在 example.php 的第 40 行

新错误消息
DOMDocument::schemaValidate() 生成的错误!
错误 1824:元素“child_integer”:“错误条件。”不是原子类型“xs:integer”的有效值。在 example.xml 的第 4 行
6
mmamsch at googlemail dot com
8 年前
对于尝试使用 PHP 验证复杂模式的人来说,需要注意一点。libxml 似乎不会自动尝试导入引用的模式,而是在没有明确导入模式的情况下,简单地跳过验证。

在我们的示例中,我们尝试使用命名空间“xttp://automotive-his.de/200706/rif”的模式来验证 XML 文件,该模式包含对命名空间“xttp://automotive-his.de/200706/rif-xhtml”的引用

<xsd:complexType name="XHTML-CONTENT">
<xsd:sequence>
<xsd:any namespace="xttp://automotive-his.de/200706/rif-xhtml"/>
</xsd:sequence>
</xsd:complexType>

它基本上表示 XHTML-CONTENT 元素可以包含 rif-xhtml 命名空间中的任何元素。

然而,由于 libxml 不知道在哪里找到模式文件,因此如果来自引用命名空间的元素,它将停止验证,即使文档包含无效的 XHTML 内容,也将通过验证。

解决方案是创建一个组合模式,该模式包含对所有与引用模式匹配的文件的导入语句

<xsd:schema xmlns:xs="xttp://www.w3.org/2001/XMLSchema">
<xs:import namespace="xttp://automotive-his.de/200706/rif-xhtml" schemaLocation="rif-xhtml.xsd"/>
<xs:import namespace="xttp://automotive-his.de/200706/rif" schemaLocation="rif.xsd"/>
<xs:import namespace="xttp://www.w3.org/XML/1998/namespace" schemaLocation="../xml.xsd"/>
</xsd:schema>

请注意,schemaLocation 告诉验证器在哪里可以找到与相应命名空间匹配的文件。当针对此组合模式验证 XML 文档时,libxml 正确地验证了 XHTML-CONTENT 内的内容。

希望这对那里的人有所帮助。
10
NetPanther
15 年前
初始情况
- Debian Lenny
- 带有 PHP 5.2.6 的 Apache 2
- libxml 2.6.32

问题:在尝试使用现有的 XML 模式验证手动创建的 (!) DOMDocument 时,我遇到了下面的警告。验证失败,即使文档是有效的,并且命令行工具 xmllint 确认了这一点(即使使用 libxml 2.6.32,所以这应该是 DOM 的问题)。验证在 libxml 2.7.3 中运行良好。

警告:DOMDocument::schemaValidate() [domdocument.schemavalidate]:元素“YourRootElementName”:没有可用于验证根的匹配全局声明。在 /var/www/YourFile.php 的第 X 行

解决方案:由于 Debian Lenny 尚未提供 libxml 2.7.3,并且这个问题似乎是由 DOM 引起的(s.o.),因此我目前在我的机器上使用以下解决方法。DOM 显然在命名空间方面存在一些问题,这些问题与手动创建的文档有关(即它们不是从文件中加载的)。

因此我的解决方法是临时保存 DOMDocument,重新加载它,然后验证临时 DOMDocument。奇怪的是,现在对同一个文档(= 相同内容)的验证有效。当然,创建临时文件不是一个好的解决方案,但是除非修复了此错误,否则此解决方法应该可以正常工作。

<?php

// 与 libxml 2.7.3 及更高版本兼容。
public function isValid()
{
return
$this->dom->schemaValidate('schema.xsd');
}

// 针对早期 libxml 版本的解决方法,例如 2.6.32。
public function isValid()
{
// 创建临时文件并手动保存创建的 DOMDocument。
$tempFile = time() . '-' . rand() . '-document.tmp';
$this->dom->save($tempFile);

// 创建临时 DOMDocument 并从文件重新加载内容。
$tempDom = new DOMDocument();
$tempDom->load($tempFile);

// 删除临时文件。
if (is_file($tempFile))
{
unlink($tempFile);
}

// 验证临时 DOMDocument。
return $tempDom->schemaValidate('schema.xsd');
}

?>
3
poletto at jeuxvideo dot com
16 年前
我在使用这种方法时遇到了一个棘手的问题,我以为是 bug,但后来我意识到自己对命名空间的理解有误。
当你想使用模式来描述 XML 文档时,你基本上将默认命名空间设置为一个个人命名空间(并在模式的 targetNamespace 属性中引用该命名空间)。

<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://my.uri.net">
<elt>
<x>blabla</x>
<y>blabla</y>
</elt>
</root>

该 xmlns 属性指定一个“默认命名空间”,表示根元素及其子元素都在此命名空间中。
我误解的地方是,DOM API 无法为根元素的每个子元素指定“默认命名空间”。
因此,你可能需要为文档中创建的每个元素或属性使用 createElementNS() 和 createAttributeNS() 方法,每次都指定命名空间的 URI("http://my.uri.net")。

这仅适用于你希望验证使用 API 构建的文档,而不是从 XML 文件或流中加载的文档。
-1
justin at redwiredesign dot com
17 年前
在他之前的评论中,Mike A 谈到了使用 XSD 验证文档。但是,你也可以在没有 XSD 的情况下进行验证。在我的情况下,我需要确保输入的内容只是有效的 XML,而不是其他内容,而且我找不到支持这种验证的 XSD。所以我就写了这个函数:

public static function validate($xml)
{
libxml_use_internal_errors(true);

$doc = new DOMDocument('1.0', 'utf-8');
$doc->loadXML($xml);

$errors = libxml_get_errors();
if (empty($errors))
{
return true;
}

$error = $errors[0];
if ($error->level < 3)
{
return true;
}

$lines = explode("\r", $xml);
$line = $lines[($error->line)-1];

$message = $error->message.' at line '.$error->line.':<br />'.htmlentities($line);

return $message;
}

这里需要注意的是,该函数只检查 LIBXML_ERR_FATAL 的第一个错误,这会破坏 XSL/XML 编译。

根据我的经验,libxml_get_errors 返回的错误按严重程度降序排列,因此这样做可能是一个不错的选择。
-2
wkoehler at ce-gmbh dot com
13 年前
在早期版本的 PHP5 中,这个函数在处理命名空间时可能会导致错误消息。我在使用 PHP 5.2.14(附带 libXML V2.6.16)时遇到了问题。切换到 PHP 5.3.5(附带 libXML V2.7.7)后,问题就消失了。我花了大约 30 个小时才找出问题所在。
To Top