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 compat layer。它也可以使用 expat,可以访问 » http://www.jclark.com/xml/expat.html 来获取。expat 自带的 Makefile 文件不会生成默认的扩展库,可以使用以下的生成规则来实现:
libexpat.a: $(OBJS) ar -rc $@ $(OBJS) ranlib $@
请访问 » http://sourceforge.net/projects/expat/ 以获取 expat 源文件的 RPM 包。
这些函数默认为有效的,它们使用了捆绑的 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 中未定义任何配置选项。
由 xml_parser_create() 和 xml_parser_create_ns() 返回的 xml 资源引用了一个 XML 解析器实例,将被用在本扩展库提供的函数中。
以下常量由本扩展模块定义,因此只有在本扩展模块被编译到 PHP 中,或者在运行时被动态加载后才有效。
XML 事件处理器定义如下:
用来设置处理器的 PHP 函数 | 事件描述 |
---|---|
xml_set_element_handler() | 元素事件(Element events)将在 XML 解析器遇到标记符的起始符或者终止符时发生。另外,对于起始符和终止符也有独立的处理器。 |
xml_set_character_data_handler() | 粗略的说,字符数据(Character data)是指 XML 文档中所有标记符以外的内容,包括标记符之间的空格。需要注意的是 XML 语法解析器不会加上或者去掉任何空格。空格的取舍将由应用程序(也就是你自己)来决定。 |
xml_set_processing_instruction_handler() | PHP 程序员对“处理指令”(Processing Instructions,PI)应该已经很熟悉了。<?php ?> 就是一个处理指令,其中 php 被称为“PI target”。除了以“XML”开头的 PI target 已被保留以外,对这些 PI 的处理将由应用程序来完成。 |
xml_set_default_handler() | 所有无法被其它处理器处理的事件将由默认处理器来处理。这些事件包括诸如 XML 和文档类型声明等内容。 |
xml_set_unparsed_entity_decl_handler() | 该处理器将在遇到无法解析的实体名称(NDATA)声明时被调用。 |
xml_set_notation_decl_handler() | 该处理器将在声明一个注释时被调用。 |
xml_set_external_entity_ref_handler() | 当 XML 解析器遇到指向外部解析的一般实体名时,该处理器将被调用。该指向的目标可以是一个文件,也可以是 URL。请参阅“外部实体名范例”。 |
元素处理函数可能会导致元素名称“大小写折叠”(case-folded)。“大小写折叠”被 XML 标准定义为“一个应用于一系列字符的过程,在该过程中,这些字符中的所有的非大写字符将被替换成它们对应大写等价字符”。换句话说,对于 XML,“大小写折叠”就是指将字符串转换成大写字符。
所有被传递给处理器函数的元素名称将默认的发生“大小写折叠”。该过程可以分别被 xml_parser_get_option() 和 xml_parser_set_option() 函数查询和控制。
以下常量被定义为 XML 的错误代码,将由 xml_parse() 返回:
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 文档的一些范例。
第一个范例用缩进格式显示一个文档中起始元素的结构。
Example#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);
?>
该范例能够高亮显示 XML 源代码。它将说明如何外部实体指向处理器来包含和解析其它文档,如何处理 PIs,以及一种确定包含有 PIs 的代码的可信度。
能被该范例使用的的 XML 文档(xmltest.xml 和 xmltest2.xml)被列在该范例之后。
Example#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);
?>
Example#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 xml: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 文件调用:
Example#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>