PHP Conference Japan 2024

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.dev.org.tw/manual/en/ref.libxml.php 以取得更多資訊。

example.xml
<?xml version="1.0"?>
<example>
<child_string>This is an example.</child_string>
<child_integer>Error condition.</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':'Error condition.' 不是原子類型 'xs:integer' 的有效值。在 example.php 第 40 行

新錯誤訊息
DOMDocument::schemaValidate() 產生錯誤!
錯誤 1824:元素 'child_integer':'Error condition.' 不是原子類型 'xs:integer' 的有效值。在 example.xml 第 4 行
6
mmamsch at googlemail dot com
9 年前
給嘗試使用 PHP 驗證複雜綱要的人的注意事項。libxml 似乎不會自動嘗試匯入參照的綱要,如果綱要未明確匯入,則只會跳過驗證。

在我們的範例中,我們嘗試針對包含對命名空間 "xttp://automotive-his.de/200706/rif-xhtml" 的參照的命名空間 "xttp://automotive-his.de/200706/rif" 的綱要驗證 XML 檔案

<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 在 jeuxvideo 點 com
16 年前
我在使用這個方法時遇到一個棘手的問題,我以為是個錯誤,但後來我意識到我誤解了一些關於命名空間的事情。
當您想要使用 schema 來描述 XML 文件時,基本上是將預設命名空間設為個人命名空間 (並且您會在 schema 的 targetNamespace 屬性中參考這個命名空間)。

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

這個 xmlns 屬性指定一個「預設命名空間」,表示 root 元素及其子元素都屬於這個命名空間。
我誤解的是,使用 DOM API 無法為 root 元素的每個子元素指定「預設命名空間」。
因此,您可能需要為文件中建立的每個元素或屬性使用 createElementNS() 和 createAttributeNS() 方法,每次都指定您命名空間的 URI ("http://my.uri.net")。

這僅適用於您想要驗證使用 API 建立的文件,而不適用於從 XML 檔案或串流載入的文件。
-1
justin 在 redwiredesign 點 com
18 年前
在之前的評論中,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.' 在第 '.$error->line.' 行:<br />'.htmlentities($line);

return $message;
}

這裡的重點是,此函數僅檢查 LIBXML_ERR_FATAL 的第一個錯誤,這會破壞 XSL/XML 編譯。

根據我的經驗,錯誤會以嚴重性遞減的順序由 libxml_get_errors 返回,所以這樣做可能是可以的。
-2
wkoehler 在 ce-gmbh 點 com
13 年前
在舊版的 PHP5 中,這個函數在處理命名空間時可能會導致錯誤訊息。我在使用 PHP 5.2.14(帶有 libXML V2.6.16)時遇到了問題。在切換到 PHP 5.3.5 與 libXML V2.7.7 後,我不再有問題了。我花了大概 30 個小時才搞清楚這一點。
To Top