|
CLXII. XML 語法解析函數
XML(eXtensible Markup Language,可增加旗標語系)是一種在 web
上進行文件交換的資料格式。該語系是由 W3C(World
Wide Web Concortium,世界萬維網組織)定義的一種標準。可以訪問
http://www.w3.org/XML/ 以取得關於 XML 及其關聯技術的更多訊息。
本增加模組可為 James Clark 的 expat
提供支援。該工具包說明解析 XML 文件(而非 XML
文件的有效化)。它支援三種原始碼的編碼模式,這三種編碼模式也被 PHP
本身所支援,它們分別是:US-ASCII、ISO-8859-1
和 UTF-8。本系統尚不支援 UTF-16。
本增加模組使會員能夠建立
XML 語法解析器,並對不同的 XML 事件定義對應的處理器。每個
XML 語法解析器都有若干個可根據需要調整的參數。
這些函數預設為有效的,它們使用了捆綁的 expat 庫。您可以通過參數 --disable-xml 來屏蔽 XML 的支援。若果您將 PHP 編譯為 Apache 1.3.9 或更高版本的一個模組, PHP 將自動使用 Apache 捆綁的 expat 庫。若果您不希望使用該捆綁的 expat 庫,請在運行 PHP 的 configure 配置腳本時使用參數 --with-expat-dir=DIR,其中 DIR 應該指向 expat 安裝的根目錄。
PHP 的 Windows
版本已經內建該增加模組的支援。無需加載任何附加增加庫即可使用這些函數。 本增加模組在 php.ini 中未定義任何配置選項。 以下常量由本增加模組定義,因此只有在本增加模組被編譯到
PHP 中,或是在運行時被動態加載後才有效。 - XML_ERROR_NONE
(integer)
- XML_ERROR_NO_MEMORY
(integer)
- XML_ERROR_SYNTAX
(integer)
- XML_ERROR_NO_ELEMENTS
(integer)
- XML_ERROR_INVALID_TOKEN
(integer)
- XML_ERROR_UNCLOSED_TOKEN
(integer)
- XML_ERROR_PARTIAL_CHAR
(integer)
- XML_ERROR_TAG_MISMATCH
(integer)
- XML_ERROR_DUPLICATE_ATTRIBUTE
(integer)
- XML_ERROR_JUNK_AFTER_DOC_ELEMENT
(integer)
- XML_ERROR_PARAM_ENTITY_REF
(integer)
- XML_ERROR_UNDEFINED_ENTITY
(integer)
- XML_ERROR_RECURSIVE_ENTITY_REF
(integer)
- XML_ERROR_ASYNC_ENTITY
(integer)
- XML_ERROR_BAD_CHAR_REF
(integer)
- XML_ERROR_BINARY_ENTITY_REF
(integer)
- XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF
(integer)
- XML_ERROR_MISPLACED_XML_PI
(integer)
- XML_ERROR_UNKNOWN_ENCODING
(integer)
- XML_ERROR_INCORRECT_ENCODING
(integer)
- XML_ERROR_UNCLOSED_CDATA_SECTION
(integer)
- XML_ERROR_EXTERNAL_ENTITY_HANDLING
(integer)
- XML_OPTION_CASE_FOLDING
(integer)
- XML_OPTION_TARGET_ENCODING
(integer)
- XML_OPTION_SKIP_TAGSTART
(integer)
- XML_OPTION_SKIP_WHITE
(integer)
元素處理函數可能會導致元素名稱「大小寫折疊」(case-folded)。「大小寫折疊」被 XML 標準定義為「一個套用於一系列字元的過程,在該過程中,這些字元中的所有的非大寫字元將被置換成它們對應大寫等價字元」。換句話說,對於 XML,「大小寫折疊」就是指將字串轉換成大寫字元。
所有被傳遞給處理器函數的元素名稱將預設的發生「大小寫折疊」。該過程可以分別被
xml_parser_get_option() 和
xml_parser_set_option() 函數查詢和控制。
以下常量被定義為 XML 的錯誤代碼,將由 xml_parse() 返回:
XML_ERROR_NONE | XML_ERROR_NO_MEMORY | XML_ERROR_SYNTAX | XML_ERROR_NO_ELEMENTS | XML_ERROR_INVALID_TOKEN | XML_ERROR_UNCLOSED_TOKEN | XML_ERROR_PARTIAL_CHAR | XML_ERROR_TAG_MISMATCH | XML_ERROR_DUPLICATE_ATTRIBUTE | XML_ERROR_JUNK_AFTER_DOC_ELEMENT | XML_ERROR_PARAM_ENTITY_REF | XML_ERROR_UNDEFINED_ENTITY | XML_ERROR_RECURSIVE_ENTITY_REF | XML_ERROR_ASYNC_ENTITY | XML_ERROR_BAD_CHAR_REF | XML_ERROR_BINARY_ENTITY_REF | XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF | XML_ERROR_MISPLACED_XML_PI | XML_ERROR_UNKNOWN_ENCODING | XML_ERROR_INCORRECT_ENCODING | XML_ERROR_UNCLOSED_CDATA_SECTION | XML_ERROR_EXTERNAL_ENTITY_HANDLING |
PHP 的 XML 增加庫支援不同字元編碼(character encoding)的
Unicode
字集。字元編碼有兩種形式,它們分別是「源編碼」(source
encoding)和「目的編碼」(target
encoding)。PHP 對文件內定表示的編碼模式是
UTF-8。
源編碼將在 XML 文件被解析後完成。源編碼可在建立一個 XML
解析器時指明(該編碼模式在 XML
解析器的生命週期中不能被再次改變)。支援的編碼模式內含
ISO-8859-1,US-ASCII
和 UTF-8。前兩種為單位元組編碼,即每個字元被一個單一的位元組表示。UTF-8
支援 1 至 4 個位元組的多 bit(最多 12)字元編碼。PHP 預設使用
ISO-8859-1 作為源編碼模式。
目的編碼將在 PHP 向 XML 處理器函數傳輸資料時被完成。當 XML
解析器被建立後,目的編碼將被設定成與源編碼相同的編碼模式,但該模式可在任何時候被變更。目的編碼將影響字元資料、旗標符名稱以及處理指令目的(PI target)。
若果 XML 解析器遇到其源編碼模式表示能力之外的字元,它將返回一個錯誤。
當 PHP 在被解析的 XML 文件中遇到現用的目的編碼無法表示的字元時,這些字元將被「降級」。簡單的說,這些字元將被問號置換。
以下是 PHP 腳本解析 XML 文件的一些範例。
第一個範例用縮進格式顯示一個文件中起始元素的結構。
例子 1. 顯示 XML 元素結構
<?php $file = "data.xml"; $depth = array();
function startElement($parser, $name, $attrs) { global $depth; for ($i = 0; $i < $depth[$parser]; $i++) { echo " "; } echo "$name\n"; $depth[$parser]++; }
function endElement($parser, $name) { global $depth; $depth[$parser]--; }
$xml_parser = xml_parser_create(); xml_set_element_handler($xml_parser, "startElement", "endElement"); if (!($fp = fopen($file, "r"))) { die("could not open XML input"); }
while ($data = fread($fp, 4096)) { if (!xml_parse($xml_parser, $data, feof($fp))) { die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser))); } } xml_parser_free($xml_parser); ?>
|
|
例子 2. 將 XML 映射為 HTML
以下範例將 XML 文件中的旗標符直接映射成 HTML
旗標符。在「映射陣列」中不存在的元素將被忽略。當然,該範例將只對一個特定的
XML 文件有效。
<?php $file = "data.xml"; $map_array = array( "BOLD" => "B", "EMPHASIS" => "I", "LITERAL" => "TT" );
function startElement($parser, $name, $attrs) { global $map_array; if (isset($map_array[$name])) { echo "<$map_array[$name]>"; } }
function endElement($parser, $name) { global $map_array; if (isset($map_array[$name])) { echo "</$map_array[$name]>"; } }
function characterData($parser, $data) { echo $data; }
$xml_parser = xml_parser_create(); // 使用大小寫折疊來保證我們能在元素陣列中找到這些元素名稱 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true); xml_set_element_handler($xml_parser, "startElement", "endElement"); xml_set_character_data_handler($xml_parser, "characterData"); if (!($fp = fopen($file, "r"))) { die("could not open XML input"); }
while ($data = fread($fp, 4096)) { if (!xml_parse($xml_parser, $data, feof($fp))) { die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser))); } } xml_parser_free($xml_parser); ?>
|
|
該範例能夠高亮顯示 XML 原始碼。它將說明如何外部實體指向處理器來包括和解析其它文件,如何處理 PIs,以及一種確定包括有 PIs 的代碼的可信度。
能被該範例使用的的 XML 文件(xmltest.xml 和
xmltest2.xml)被列在該範例之後。
例子 3. 外部實體範例
<?php $file = "xmltest.xml";
function trustedFile($file) { // only trust local files owned by ourselves if (!eregi("^([a-z]+)://", $file) && fileowner($file) == getmyuid()) { return true; } return false; }
function startElement($parser, $name, $attribs) { echo "<<font color=\"#0000cc\">$name</font>"; if (count($attribs)) { foreach ($attribs as $k => $v) { echo " <font color=\"#009900\">$k</font>=\"<font color=\"#990000\">$v</font>\""; } } echo ">"; }
function endElement($parser, $name) { echo "</<font color=\"#0000cc\">$name</font>>"; }
function characterData($parser, $data) { echo "<b>$data</b>"; }
function PIHandler($parser, $target, $data) { switch (strtolower($target)) { case "php": global $parser_file; // If the parsed document is "trusted", we say it is safe // to execute PHP code inside it. If not, display the code // instead. if (trustedFile($parser_file[$parser])) { eval($data); } else { printf("Untrusted PHP code: <i>%s</i>", htmlspecialchars($data)); } break; } }
function defaultHandler($parser, $data) { if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") { printf('<font color="#aa00aa">%s</font>', htmlspecialchars($data)); } else { printf('<font size="-1">%s</font>', htmlspecialchars($data)); } }
function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId, $publicId) { if ($systemId) { if (!list($parser, $fp) = new_xml_parser($systemId)) { printf("Could not open entity %s at %s\n", $openEntityNames, $systemId); return false; } while ($data = fread($fp, 4096)) { if (!xml_parse($parser, $data, feof($fp))) { printf("XML error: %s at line %d while parsing entity %s\n", xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser), $openEntityNames); xml_parser_free($parser); return false; } } xml_parser_free($parser); return true; } return false; }
function new_xml_parser($file) { global $parser_file;
$xml_parser = xml_parser_create(); xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1); xml_set_element_handler($xml_parser, "startElement", "endElement"); xml_set_character_data_handler($xml_parser, "characterData"); xml_set_processing_instruction_handler($xml_parser, "PIHandler"); xml_set_default_handler($xml_parser, "defaultHandler"); xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
if (!($fp = @fopen($file, "r"))) { return false; } if (!is_array($parser_file)) { settype($parser_file, "array"); } $parser_file[$xml_parser] = $file; return array($xml_parser, $fp); }
if (!(list($xml_parser, $fp) = new_xml_parser($file))) { die("could not open XML input"); }
echo "<pre>"; while ($data = fread($fp, 4096)) { if (!xml_parse($xml_parser, $data, feof($fp))) { die(sprintf("XML error: %s at line %d\n", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser))); } } echo "</pre>"; echo "parse complete\n"; xml_parser_free($xml_parser);
?>
|
|
例子 4. xmltest.xml <?xml version='1.0'?>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml">
]>
<chapter>
<TITLE>Title &plainEntity;</TITLE>
<para>
<informaltable>
<tgroup cols="3">
<tbody>
<row><entry>a1</entry><entry morerows="1">b1</entry><entry>c1</entry></row>
<row><entry>a2</entry><entry>c2</entry></row>
<row><entry>a3</entry><entry>b3</entry><entry>c3</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>
&systemEntity;
<section id="about">
<title>About this Document</title>
<para>
<!-- this is a comment -->
<?php echo 'Hi! This is PHP version ' . phpversion(); ?>
</para>
</section>
</chapter> |
|
以下文件將被 xmltest.xml 檔案呼叫:
例子 5. xmltest2.xml <?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY testEnt "test entity">
]>
<foo>
<element attrib="value"/>
&testEnt;
<?php echo "This is some more PHP code being executed."; ?>
</foo> |
|
| |