阿辉的博客

系统 网络 集群 数据库 分布式云计算等 研究

利用php DOM函数库创建xml文档

<?php

/**
* filename: domEx.php
*
* Editor: richard_ma
*
* Date: 2007-07-27
*
* Description:
*   利用php DOM函数库创建xml文档
*/

// 设置Http头属性为xml
header("Content-Type:text/xml");

/**
* 创建文档对象及根节点
*/
// 创建DOM对象,xml版本为1.0编码方式为UTF-8
$dom = new DOMDocument(‘1.0’, ‘UTF-8’);
// 创建节点
$response = $dom->createElement(‘rootNode’);
// 将节点作为子节点加入xml文档中
$dom->appendChild($response);

/**
* 创建属性
*/
// 创建属性节点
$resAttribute = $dom->createAttribute(‘attrNode’);
// 插入属性节点
$response->appendChild($resAttribute);
// 创建属性值
$attrValue = $dom->createTextNode(‘attrValue’);
// 插入属性值
$resAttribute->appendChild($attrValue);

/**
* 创建标签内容
*/
// 创建文字节点
$resContent = $dom->createTextNode(‘textNode’);
// 将文字节点作为子节点加入根节点中
$response->appendChild($resContent);

// 导出xml字符串
$xmlStr = $dom->saveXML();

// 输出xml字符串
echo $xmlStr;

?>

使用PHP的DOM functins读到RSS的新闻的实例

<?php
/*
*@作者:旭日
*@Email:54ano@163.com
*/

/*
*@实例化一个DOM类
*@version:1.0
*encoding:gb2312
*/
$dom = new DomDocument(‘1.0’, ‘gb2312’);
/*
*@载个文件
*/
$dom->load(‘http://rss.cnfol.com/news.xml’);
/*
*@获取channel节点
*/
$items = $dom->getElementsByTagName(‘channel’);

/*=====如果是中文内容,必须经过iconv编码转换,否则显示为乱码=====*/

/*
*@采channel下的子节点descrīption等节点的内容
*/
foreach (Array(‘descrīption’, ‘title’, ‘link’, ‘language’, ‘lastBuildDate’, ‘generator’, ‘copyright’) AS $node) {
   $global[‘cont’][$node] = iconv(‘utf-8’, ‘gb2312’, $items->item(0)->getElementsByTagName($node)->item(0)->nodeValue);
}
/*
*@采channel下的子节点image节点的信息
*/
foreach (Array(‘url’, ‘link’, ‘title’, ‘descrīption’) AS $node) {
   $global[‘cont’][‘image’][$node] = iconv(‘utf-8’, ‘gb2312’, $items->item(0)->getElementsByTagName(‘image’)->item(0)->getElementsByTagName($node)->item(0)->nodeValue);
}
/*
*@采channel下的子节点item节点的信息
*/
foreach ($items->item(0)->getElementsByTagName(‘item’) AS $key => $item) {
   foreach (Array(‘title’, ‘descrīption’, ‘link’, ‘author’, ‘pubDate’) AS $node) {
       $global[‘cont’][‘items’][$key][$node] = iconv(‘utf-8’, ‘gb2312’, $item->getElementsByTagName($node)->item(0)->nodeValue);
   }
}
print_r($global);
?>

定界符<<<的用法

定界符
给字符串定界的方法使用定界符语法(“<<<”)。应该在 <<< 之后提供一个标识符,然后是字符串,然后是同样的标识符结束字符串。
结束标识符必须从行的第一列开始。同样,标识符也必须遵循 PHP 中其它任何标签的命名规则:只能包含字母数字下划线,而且必须以下划线或非数字字符开始。

举个例子:
<?php
$str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;
?>

但要注意的是:
结束标识符所在的行不能包含任何其它字符,可能除了一个分号(;)之外。这尤其意味着该标识符不能被缩进,而且在分号之前和之后都不能有任何空格或制表 符。同样重要的是要意识到在结束标识符之前的第一个字符必须是你的操作系统中定义的换行符。例如在 Macintosh 系统中是 r。 如果破坏了这条规则使得结束标识符不“干净”,则它不会被视为结束标识符,PHP 将继续寻找下去。如果在这种情况下找不到合适的结束标识符,将会导致一个在脚本最后一行出现的语法错误。

ps:定界符文本表现的就和双引号字符串一样,只是没有双引号。这意味着在定界符文本中不需要转义引号,不过仍然可以用以上列出来的转义代码。

例一:

<?php
echo <<< EOT
                  <table width=80% border="2" cellpadding="3" cellspacing="0" bordercolor="#808080">
                 <tr bgcolor="#84A9E1">
                 <td align="center">ClassID</td>
                 <td align="center">stuno</td>
                 <td   align="center">学生姓名</td>
                 <td align="center">家长姓名</td>
                 <td align="center">家长手机号</td>
                 </tr>
EOT;
?>

例二:

<?
               $xml=<<<EOT
<message name="getTermRequest">
<part name="term" type="xs:string"/>
</message>

<message name="getTermResponse">
<part name="value" type="xs:string"/>
</message>

<portType name="glossaryTerms">
<operation name="getTerm">
<input message="getTermRequest"/>
<output message="getTermResponse"/>
</operation>
</portType>
EOT;

echo $xml;

?>

使用php-java-bridge让PHP5支持java

环境:
       服务器是64位的。
       centos linux 5.0 (x86_64)
       系统自带apache 2.2及php5.x

所需安装包(latest version):
jdk-1_5_0_12-linux-amd64.bin (http://java.sun.com/j2se/1.5.0/download.jsp)
php-java-bridge_5.0.0.tar.gz (http://php-java-bridge.sourceforge.net/)

1.安装jdk-1_5_0_05

下载地址:http://java.sun.com/j2se/1.5.0/download.jsp

cp /path/to/ jdk-1_5_0_12-linux-amd64.bin /usr/local/
cd /usr/local
chmod +x jdk-1_5_0_12-linux-amd64.bin
./ jdk-1_5_0_12-linux-amd64.bin
ln -s jdk1.5.0_12 jdk

2.设置环境变量,java的运行需要设置一下环境变量。

在/etc/profile中设置如下参数:

JAVA_HOME=/usr/local/java
PATH=$PATH:$JAVA_HOME/bin

并且export它们。
修改原来的export语句为:

export JAVA_HOME PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC

若要立即生效,在shell下边依次执行一遍上边的语句。
输入java -version能看到版本信息,即安装java成功了。

3.安装php-java-bridge_5.0.0.tar.gz

下载地址 http://php-java-bridge.sourceforge.net/
tar php-java-bridge_5.0.0.tar.gz
cd php-java-bridge-5.0.0
(具体环境要求和安装请阅读INSTALL文档)
phpize
./configure –with-java=$JAVA_HOME
make && make install
编辑php.ini文件
增加
[Java]
java.java_home="/usr/local/java"
java.java="/usr/local/java/jre/bin/java"
java.log_file="/var/log/php-java-bridge.log"
java.classpath="/usr/lib64/php/modules/JavaBridge.jar"
java.libpath="/usr/lib64/php/modules"
extension_dir="/usr/lib64/php/modules/"
extension=java.so

验证:

重启Apache ,用pstree查看,有“httpd—java—java—8*[java]”进程。
用命令行方式检测 echo ‘<?php phpinfo() ?>’ | php | fgrep java ,应该返回字样有“java status => running”
通过Web方式查看phpinfo() ,存在Java小节。

在访问目录下创建java.php文件
<?php

phpinfo();

print "nn";

$v = new java("java.lang.System");

$arr=$v->getProperties();

foreach ($arr as $key => $value) {

print $key . " -> " . $value . "<br>n";

}

?>

通过Web访问,能正确显示Java版本、操作系统、系统时间等信息,说明执行成功。

PHP中xajax库中文问题及提示返回XML文件无效的解决办法

xajax是PHP下一个非常好的ajax框架,虽然xajax自己说自己是一个库(Library),但是我觉得它更像一个框架(Framework)。是一个开源的 PHP 类库 它能够让你黏合HTML、CSS、Javascript和,并可以结合Smarty模板系统使用。

在运用Xoops的Page模组的时候,发现其不能和中文正常工作,特别是不能和UTF-8的 页面一起工作,昨天在利用这个实现DataGrid的时候发现完全不能正常工作了,一直报“the XML response that was returned from the server is invalid”这个错误,Google了一下,发现不少人也有这个问题。结合之前解决Xoops的Page模组(感谢文明猪)的UTF-8使用问题的方 法,终于发现了完美解决库使用的方法。

如果页面采用GB2312或GBK作为编码或字符集,一般网络上面提到的方法可以解决:需要注意的是common.php、server.php及server.php用到的php文件(特别是生成数据的文件)全部不要使用UTF-8格式保存而使用ASCII格式保存。同时server.输出的数据内容最好也采用gb2312
在common.前面定义
define(’_DEFAULT_CHAR_ENCODING’,’GB2312′);
再在调用时使用:
$xajax = new ();    
$->decodeUTF8InputOn();
或者
$xajax = new (’gb2312′);    
$->setCharEncoding();  
$->decodeUTF8InputOn();    
$objResponse = new xajaxResponse(’gb2312′);

如果页面采用UTF-8字符集的话,网上一些人说使用UTF-8格式编码就可以了,实际上即使全部使用UTF-8也不能正常工作,还是会报错:“the XML response that was returned from the server is invalid”。联想到之前在IE下碰到UTF-8 BOM Bug(文章:IE下页面无端端空出一行及utf8页面无法显示的解决方法 ), 会不会这个地方服务器传回的XML文件中也包含多个UTF-8 BOM内容呢?通过ethereal截获数据包发现,服务器返回的内容中还真是包含了多个UTF-8 BOM,可能还是IE使用的Microsoft XML Parser的UTF-8 BOM Bug吧。这样解决办法就出来了:所有页面全部采用UTF8并使用无UTF-8 BOM保存(UltraEdit 操作方法:F12或另存为界面中格式下拉框选择‘UTF-8-无 BOM’),页面包括库文件、common.php文件、server.文件。供访问的页面也可以使用无UTF-8 BOM保存。

有时候返回的参数(特别是部分)被url编码了,这个时候需要我们将返回的数据使用Urldecode进行处理,以便能够正常完成后面的数据库操作。

最新 0.25下载地址:http://prdownloads.sourceforge.net/xajax/xajax_0.2.5.zip?download

xajax中文手册(HonestQiao第一版,FlyingHail修改版) http://www.flyinghail.net/?p=44

UTF-8 BOM解释:”EF BB BF” 这三个字节就叫BOM,BOM的全称叫做”Byte Order Mard”。在UTF-8文件中常用BOM来表明这个文件是UTF-8文件,而BOM的本意实在UTF-16中用来表示高低字节序列的。

另外,PHP网站上说使用–enable-zend-multibyte可以解决这类问题,不过没有尝试,并且预计在 6.0中会对Unicode做完美的支持。

php5 xml学习之xslt

看了下,php5的XSLT十分简单,举例子说明之.

首先是XML

<?xml version=’1.0′ ?>
<contacts>
<contact idx="37">
<name>Ramsey White II</name>
<category>Family</category>
<phone type="home">301-555-1212</phone>
<meta id="x634724" />
</contact>
<contact idx="42">
<name>Stratis Kakadelis</name>
<category>Friends</category>
<phone type="home">240-555-1212</phone>
<phone type="work">410-555-7676</phone>
<email>skak@example.com</email>
<meta id="y49302" />
</contact>
<contact idx="57">
<name>Kelly Williamson</name>
<category>Friends</category>
<phone type="cell">443-555-9999</phone>
<email>kwill@example.com</email>
<email>dynky@tech.example.com</email>
<meta id="w4r302" />
</contact>
</contacts> 之后是XSLT

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match="contacts">
<html><head><title>Contacts!</title></head><body>
<div style="border: 2px solid blue; padding: 5px;">
<h1>Contacts:</h1>
<xsl:apply-templates />
</div></body></html>
</xsl:template>
<xsl:template match="contact">
<div style="border: 1px solid black; margin: 20px; padding: 5px;">
<h2><xsl:value-of select="name" /></h2>
<p>
Home Phone: <xsl:value-of select="phone[@type=’home’]" /><br />
Work Phone: <xsl:value-of select="phone[@type=’work’]" /><br />
Cell Phone: <xsl:value-of select="phone[@type=’cell’]" /><br />
</p>
</div>
</xsl:template>
</xsl:stylesheet>
最后是调用的php,这里用的是dom

<?php
// Using the DOM extension, load the XML file into memory:
$dom = new DOMDocument();
$dom->load(‘contacts.xml’);
// Now also load the XSL file as well:
$xsl = new DOMDocument();
$xsl->load(‘contacts.xsl’);
// Create a new XSLT Processor
$proc = new XSLTProcessor;
// Import the XSL styles into this processor
$proc->importStyleSheet($xsl);
// Now transform the XML file and echo it to the screen!
echo $proc->transformToXML($dom);
?>

解析大型或复杂 XML 文档的 PHP5 技术

简介

PHP5 提供了更多的 XML 解析技术。James Clark 的 Expat SAX 解析器(现在以 libxml2 为基础)不再是惟一功能完备的解析器。经常需要使用完全符合 W3C 标准的 DOM 解析器进行解析。无论第 1 部分(请参阅 参考资料) 介绍的 SimpleXML 还是比 SAX 更简单更快捷的 XMLReader 都提供了另外的解析方法。所有这些 XML 扩展现在都以 GNOME 项目的 libxml2 库为基础。这个统一的库考虑了不同扩展之间的互操作性。本文将介绍 PHP5 XML 解析技术,特别是大型、复杂 XML 文档的解析。还介绍了关于解析技术的一些背景知识,何种方法最适合于何种类型的 XML 文档,如果要作出选择,则应依据何种标准。



回页首

SimpleXML

请访问 面向 Perl 和 PHP 开发人员的 XML:您可以通过该专题来了解更多与 Perl 和 PHP 相关的 XML 技术。

第 1 部分介绍了 XML 的基本信息,主要介绍简单的、入门级的应用程序编程接口(Application Programming Interfaces,API)。通过例子说明对于处理简单、可预测并且不大的 XML 文档,SimpleXML(必要的时候与文档对象模型(DOM)结合使用)是一种理想的选择。

XML 和 PHP5

可扩展标记语言(Extensible Markup Language,XML)不仅被看作是一种标记语言,而且是一种基于文本的数据存储格式,它提供了基于文本的方法来应用和描述信息的树状结构。

PHP5 提供了一些全新的和重新编写的 XML 解析扩展。其中包括将整个 XML 文档加载到内存中的 SimpleXML、DOM 和 XSLT 处理程序。也有每次把 XML 文档的一部分加载到内存中的 Simple API for XML (SAX) 和 XMLReader。SAX 的功能和在 PHP4 中没有变化,但不再以 expat 库为基础而改用了 libxml2 库。如果通过其他语言熟悉了 DOM,则与以前的版本相比,在 PHP5 中使用 DOM 编程将简单得多。



回页首

XML 解析基础

解析 XML 有两种基本的方式:树和流。树解析方式需要将整个 XML 文档加载到内存中。树文件结构允许随机访问文档元素和编辑 XML。树型解析的例子包括 DOM 和 SimpleXML。这些解析器都在内存中以不同但可互操作的格式共享树状结构。和树解析方式不同,流解析不需要将整个文档加载到内存中。这里的流和流音 频中的流意思很相近。其用途和目的都一样,就是每次提交少量数据以节约带宽和内存。在流解析中,只能访问当前解析的节点,并且不能将 XML 作为一个文档来编辑。流解析器的例子包括 XMLReader 和 SAX。



回页首

基于树的解析器

之所以称为基于树的解析器,是因为它们将整个 XML 文档加载到内存中,并把文档的根作为主干,把所有的儿子、孙子和它们的后代以及属性作为分支。最熟悉的基于树的解析器是 DOM。编码最简单的基于树的解析器是 SimpleXML。后面对两者都将作出介绍。

使用 DOM 解析

根据 W3C 的定义,DOM 标准是 “……一种平台和语言中立的接口,能够让程序和脚本动态地访问和更新文档的内容、结构和样式。” GNOME 项目的 libxml2 库用 C 实现了 DOM 及其全部方法。因为所有的 PHP5 XML 扩展都基于 libxml2,所以彼此之间具有完全的互操作性。这种互操作性大大增强了它们的功能。比方说,可以使用流解析器 XMLReader 获取一个元素,将其导入 DOM,然后用 XPath 提取数据。这就大大增加了灵活性。清单 5 给出了一个例子。

DOM 是基于树的解析器。DOM 很容易理解和使用,因为其内存结构与原始 XML 文档相似。DOM 通过创建对象树来向应用程序传递信息,它完全复制了 XML 文件的元素树,每个 XML 元素都是树上的一个节点。DOM 是一种 W3C 标准,由于和其他编程语言的一致性,对于开发人员来说,为 DOM 增加了不少权威性。因为 DOM 要创建整个文档的树,要占用大量内存和处理器时间。

使用 DOM

如果由于受设计或者其他因素的限制必须在解析器领域内耍点小聪明的话,则仅仅从灵活的角度来看应该选择 DOM。使用 DOM 可以构建、修改、查询、验证和转换 XML 文档。可以利用所有的 DOM 方法和属性。多数 DOM level 2 方法的实现都有适当的属性支持。由于非凡的灵活性,使用 DOM 可以解析任意复杂的文档。但是要记住,如果要把很大的 XML 文档一次加载到内存中,则取得灵活性的代价相当高昂。

清单 1 中的例子用 DOM 解析文档,通过 getElementById 检索一个元素。引用 ID 之前需要设置 validateOnParse=true 来验证文档。根据 DOM 标准,它要求 DTD 定义一个 ID 类型的属性 ID。

清单 1. 使用 DOM 处理基本文档
<?php

$doc = new DomDocument;

// We must validate the document before referring to the id
$doc->validateOnParse = true;
$doc->Load(‘basic.xml’);

echo "The element whose id is myelement is: " .
$doc->getElementById(‘myelement’)->tagName . "n";

?>

getElementsByTagName() 函数返回类 DOMNodeList 的一个新实例,包含使用给定标记名的元素。当然这个列表需要遍历。迭代 getElementsByTagName() 返回的 NodeList 的同时修改文档结构,会影响迭代的 NodeList(参见清单 2)。不需要验证。

清单 2. DOM getElementsByTagName 方法
DOMDocument {
DOMNodeList getElementsByTagName(string name);
}

清单 3 中的例子结合使用了 DOM 和 XPath。

清单 3. 使用 DOM 和 XPath 进行解析
<?php

$doc = new DOMDocument;

// We don’t want to bother with white spaces
$doc->preserveWhiteSpace = false;

$doc->Load(‘book.xml’);

$xpath = new DOMXPath($doc);

// We start from the root element
$query = ‘//book/chapter/para/informaltable/tgroup/tbody/row/entry[. = "en"]’;

$entries = $xpath->query($query);

foreach ($entries as $entry) {
echo "Found {$entry->previousSibling->previousSibling->nodeValue}," .
" by {$entry->previousSibling->nodeValue}n";
}
?>

说了 DOM 的这么多优点之后,为了强调我的观点,最后再给出一个错误使用 DOM 的例子,然后在后面的例子中解决这个问题。清单 4 中的例子将一个很大的文件加载到 DOM 中,只是为了用 DomXpath 从一个属性中提取数据。

清单 4. 错误使用 DOM 和 XPath,用于大型 XML 文档
<?php

// Parsing a Large Document with DOM and DomXpath
// First create a new DOM document to parse
$dom = new DomDocument();

// This document is huge and we don’t really need anything from the tree
// This huge document uses a huge amount of memory
$dom->load("tooBig.xml");
$xp = new DomXPath($dom);
$result = $xp->query("/blog/entries/entry[@ID = 5225]/title") ;
print $result->item(0)->nodeValue ."n";

?>

后面清单 5 中的例子仍然使用 DOM 和 XPath,但每次让 XMLReader 用 expand() 传递一个元素的数据。通过这种方法就能将 XMLReader 传递的节点转换成 DOMElement

清单 5. 正确使用 DOM 和 XPath,用于大型 XML 文档
<?php

// Parsing a large document with XMLReader with Expand – DOM/DOMXpath
$reader = new XMLReader();

$reader->open("tooBig.xml");

while ($reader->read()) {
switch ($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName == "entry") {
if ($reader->getAttribute("ID") == 5225) {
$node = $reader->expand();
$dom = new DomDocument();
$n = $dom->importNode($node,true);
$dom->appendChild($n);
$xp = new DomXpath($dom);
$res = $xp->query("/entry/title");
echo $res->item(0)->nodeValue;
}
}
}
}

?>

使用 SimpleXML 解析

SimpleXML 扩展是另外一种 XML 文档解析方法。SimpleXML 扩展需要用到 PHP5 并包括内置的 XPath 支持。SimpleXML 最适合处理不复杂的、基本的 XML 数据。如果 XML 文档不是很复杂、层次不深、没有混合内容,则与 DOM 相比 SimpleXML 更简单,正如其名称所暗示的那样。如果处理的文档结构是已知的,就会更加直观。

使用 SimpleXML

SimpleXML 具有 DOM 的很多优点,但是编码更加简单。它允许轻松地访问 XML 树,具有内置的验证机制和 XPath 支持,能够与 DOM 互操作,为其提供读写 XML 文档的支持。可以简单迅速地处理使用 SimpleXML 解析的文档。但是要记住,和 DOM 一样,SimpleXML 的易用性和灵活性的代价也是无法向内存中加载大型 XML 文档。

清单 6 中的代码从示例 XML 中提取 <plot>。

清单 6. 提取 plot 文本
<?php
$xmlstr = <<<XML
<?xml version=’1.0′ standalone=’yes’?>
<books>
<book>
<title>Great American Novel<title>
<plot>
Cliff meets Lovely Woman. Loyal Dog sleeps, but
wakes up to bark at mailman.
</plot>
<success type="bestseller">4<success>
<success type="bookclubs">9<success>
</book>
<books>
XML;
?>
<?php

$xml = new SimpleXMLElement($xmlstr);
echo $xml->book[0]->plot; // "Cliff meets Lovely Woman. …"
?>

另一方面,也许还要提取分为多行的地址。如果同一个父元素中存在同一元素的多个实例,通常需要使用迭代技术。清单 7 中的代码演示了此项功能。

清单 7. 提取元素的多个实例
<?php
$xmlstr = <<<XML
<xml version=’1.0′ standalone=’yes’?>
<books>
<book>
<title>Great American Novel<title>
<plot>
Cliff meets Lovely Woman.
<plot>
<success type="bestseller">4<success>
<success type="bookclubs">9</success>
<book>
<book>
<title>Man Bites Dog</title>
<plot>
Reporter invents a prize-winning story.
</plot>
<success type="bestseller">22<success>
<success type="bookclubs">3<success>
<book>
</books>
XML;
?>
<php

$xml = new SimpleXMLElement($xmlstr);

foreach ($xml->book as $book) {
echo $book->plot, ‘<br />’;
}
?

除了读取元素名称及其值以外,SimpleXML 也能访问元素的属性。清单 8 所示的代码中,可以像访问数组元素一样访问元素的属性。

清单 8. SimpleXML 访问元素的属性的演示
<?php
$xmlstr = <<<XML
<?xml version=’1.0′ standalone=’yes’?>
<books>
<book>
<title>Great American Novel</title>
<plot>
Cliff meets Lovely Woman.
</plot>
<success type="bestseller">4</success>
<success type="bookclubs">9</success>
</book>
<book>
<title>Man Bites Dog</title>
<plot>
Reporter invents a prize-winning story.
<plot>
<success type="bestseller">22<success>
<success type="bookclubs">3</success>
</book>
<books>
XML;
?>
<?php

$xml = new SimpleXMLElement($xmlstr);

foreach ($xml->book[0]->success as $success) {
switch((string) $success[‘type’]) {
case ‘bestseller’:
echo $success, ‘ months on bestseller list<br />’;
break;
case ‘bookclubs’:
echo $success, ‘ bookclub listings<br />’;
break;
}
}

?>

最后一个例子(参见清单 9)结合使用了 SimpleXML 以及 DOM 和 XMLReader。通过 XMLReader,每次使用 expand() 传递一个元素的数据。这样就能将 XMLReader 传递的节点转换成 DOMElement 再传递给 SimpleXML 了。

清单 9. 结合 SimpleXML 以及 DOM 和 XMLReader 解析大型 XML 文档

<?php

// Parsing a large document with Expand and SimpleXML
$reader = new XMLReader();

$reader->open("tooBig.xml");

while ($reader->read()) {
switch ($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName == "entry") {
if ($reader->getAttribute("ID") == 5225) {
$node = $reader->expand();
$dom = new DomDocument();
$n = $dom->importNode($node,true);
$dom->appendChild($n);
$sxe = simplexml_import_dom($n);
echo $sxe->title;
}
}
}
}

?>



回页首

基于流的解析器

之所以称为基于流的解析器,是因为它们采用和流式音频同样的原理来解析 XML 流,处理一个特殊节点,完成之后则将其完全忘掉。XMLReader 是一种 pull 解析器,其编码方法和数据库查询结果表的游标中非常类似。因此更容易处理不熟悉的或者不可预测的 XML 文件。

使用 XMLReader 解析

XMLReader 扩展是一种基于流的解析器,其类型通常被称为游标类型或者 pull 类型解析器。XMLReader 根据请求从 XML 文档中获取信息。它是基于派生自 C# XmlTextReader 的 API。PHP 5.1 默认包含并启用它,基于 libxml2。在 PHP 5.1 之前,默认不启用 XMLReader 扩展,但是可从 PECL(请参阅 参考资料 中的相关链接)下载。XMLReader 支持名称空间和验证,包括 DTD 和 Relaxed NG。

使用 XMLReader

XMLReader 作为一种流解析器,非常适合解析大型 XML 文档,编码比 SAX 更简单,而且通常速度也快。这是理想的流解析器。

清单 10 中的例子使用 XMLReader 解析大型 XML 文档。

清单 10. XMLReader 解析大型 XML 文件
<?php

$reader = new XMLReader();
$reader->open("tooBig.xml");
while ($reader->read()) {
switch ($reader->nodeType) {
case (XMLREADER::ELEMENT):
if ($reader->localName == "entry") {
if ($reader->getAttribute("ID") == 5225) {
while ($reader->read()) {
if ($reader->nodeType == XMLREADER::ELEMENT) {
if ($reader->localName == "title") {
$reader->read();
echo $reader->value;
break;
}
if ($reader->localName == "entry") {
break;
}
}
}
}
}
}
}
?>

使用 SAX 解析

Simple API for XML (SAX) 是一种流解析器。事件与读入的 XML 文档相关联,因此 SAX 以回调的方式编码。元素打开关闭标记、元素内容、实体和解析错误都有对应的事件。使用 SAX 解析器而不是 XMLReader 的主要原因在于 SAX 解析器有时候效率更高一些,而且通常更被人们熟悉。SAX 解析器的主要缺点是代码很复杂,比 XMLReader 代码编写起来更难。

使用 SAX

SAX 对于那些曾经在 PHP4 中处理过 XML 的人来说可能比较熟悉,PHP5 中的 SAX 扩展与过去的版本兼容。由于也是流解析器,因此非常适合处理大型文件,但是比不上 XMLReader。

清单 11 中的例子使用 SAX 解析大型 XML 文档。

清单 11. 使用 SAX 解析大型 XML 文件
<?php

//This class contains all the callback methods that will actually
//handle the XML data.
class SaxClass {
private $hit = false;
private $titleHit = false;

//callback for the start of each element
function startElement($parser_object, $elementname, $attribute) {
if ($elementname == "entry") {
if ( $attribute[‘ID’] == 5225) {
$this->hit = true;
} else {
$this->hit = false;
}
}
if ($this->hit && $elementname == "title") {
$this->titleHit = true;
} else {
$this->titleHit =false;
}
}

//callback for the end of each element
function endElement($parser_object, $elementname) {
}

//callback for the content within an element
function contentHandler($parser_object,$data)
{
if ($this->titleHit) {
echo trim($data)."<br />";
}
}
}

//Function to start the parsing once all values are set and
//the file has been opened
function doParse($parser_object) {
if (!($fp = fopen("tooBig.xml", "r")));

//loop through data
while ($data = fread($fp, 4096)) {
//parse the fragment
xml_parse($parser_object, $data, feof($fp));
}
}

$SaxObject = new SaxClass();
$parser_object = xml_parser_create();
xml_set_object ($parser_object, $SaxObject);

//Don’t alter the case of the data
xml_parser_set_option($parser_object, XML_OPTION_CASE_FOLDING, false);

xml_set_element_handler($parser_object,"startElement","endElement");
xml_set_character_data_handler($parser_object, "contentHandler");

doParse($parser_object);

?>



回页首

结束语

共享这篇文章……

digg 将本文提交到 Digg del.icio.us 发布到 del.icio.us

PHP5 提供了多种改进的解析技术。最常见的有 DOM,它现在完全与 W3C 标准兼容,适合复杂但是相对较小的文档。SimpleXML 适合简单而且不太大的 XML 文档,XMLReader 比 SAX 更简单、更快,是适合大型文档的流解析器。

在PHP5中使用DOM控制XML

PHP5中增强了XML的支持,使用DOM扩展了XML操作的能耐。这些函数作为 PHP5 核心的一部分,无需被安装即可使用。 6KQLinux联盟
   6KQLinux联盟
   下面的例子简单的演示了DOM对XML的操作,详细解释请看代码中的注释 6KQLinux联盟
   6KQLinux联盟
   <? 6KQLinux联盟
   /************************************************ 6KQLinux联盟
   ** use XML in PHP5 6KQLinux联盟
   ** reference site: 6KQLinux联盟
   ** http://cn.php.net/manual/zh/ref.dom.php 6KQLinux联盟
   ** the follow codes need PHP5 support 6KQLinux联盟
   ** 6KQLinux联盟
   *************************************************/ 6KQLinux联盟
   6KQLinux联盟
   6KQLinux联盟
   //首先要创建一个DOMDocument对象 6KQLinux联盟
   $dom = new DomDocument(); 6KQLinux联盟
   //然后载入XML文件 6KQLinux联盟
   $dom -> load("test.xml"); 6KQLinux联盟
   6KQLinux联盟
   //输出XML文件 6KQLinux联盟
   //header("Content-type: text/xml;charset=gb2312"); 6KQLinux联盟
   //echo $dom -> saveXML(); 6KQLinux联盟
   6KQLinux联盟
   //保存XML文件,返回值为int(文件大小,以字节为单位) 6KQLinux联盟
   //$dom -> save("newfile.xml"); 6KQLinux联盟
   6KQLinux联盟
   echo "<hr/>取得所有的title元素:<hr/>"; 6KQLinux联盟
   $titles = $dom -> getElementsByTagName("title"); 6KQLinux联盟
   foreach ($titles as $node) 6KQLinux联盟
   { 6KQLinux联盟
   echo $node -> textContent . "<br/>"; 6KQLinux联盟
   //这样也可以 6KQLinux联盟
   //echo $node->firstChild->data . "<br/>"; 6KQLinux联盟
   } 6KQLinux联盟
   6KQLinux联盟
   /* 6KQLinux联盟
   echo "<hr/>从根结点遍历所有结点:<br/>"; 6KQLinux联盟
   foreach ($dom->documentElement->childNodes as $items) { 6KQLinux联盟
   //如果节点是一个元素(nodeType == 1)并且名字是item就继续循环 6KQLinux联盟
   if ($items->nodeType == 1 && $items->nodeName == "item") { 6KQLinux联盟
   foreach ($items->childNodes as $titles) { 6KQLinux联盟
   //如果节点是一个元素,并且名字是title就打印它. 6KQLinux联盟
   if ($titles->nodeType == 1 && $titles->nodeName == "title") { 6KQLinux联盟
   print $titles->textContent . "n"; 6KQLinux联盟
   } 6KQLinux联盟
   } 6KQLinux联盟
   } 6KQLinux联盟
   } 6KQLinux联盟
   */ 6KQLinux联盟
   6KQLinux联盟
   //使用XPath查询数据 6KQLinux联盟
   echo "<hr/>使用XPath查询的title节点结果:<hr/>"; 6KQLinux联盟
   $xpath = new domxpath($dom); 6KQLinux联盟
   $titles = $xpath->query("/rss/channel/item/title"); 6KQLinux联盟
   foreach ($titles as $node) 6KQLinux联盟
   { 6KQLinux联盟
   echo $node->textContent."<br/>"; 6KQLinux联盟
   } 6KQLinux联盟
   /* 6KQLinux联盟
   这样和使用getElementsByTagName()方法差不多,但是Xpath要强大的多 6KQLinux联盟
   深入一点可能是这样: 6KQLinux联盟
   /rss/channel/item[position() = 1]/title 返回第一个item元素的所有 6KQLinux联盟
   /rss/channel/item/title[@id = ’23’] 返回所有含有id属性并且值为23的title 6KQLinux联盟
   /rss/channel/&folder&/title 返回所有articles元素下面的title(译者注:&folder&代表目录深度) 6KQLinux联盟
   */ 6KQLinux联盟
   6KQLinux联盟
   6KQLinux联盟
   //向DOM中写入新数据 6KQLinux联盟
   $item = $dom->createElement("item"); 6KQLinux联盟
   $title = $dom->createElement("title"); 6KQLinux联盟
   $titleText = $dom->createTextNode("title text"); 6KQLinux联盟
   $title->appendChild($titleText); 6KQLinux联盟
   $item->appendChild($title); 6KQLinux联盟
   $dom->documentElement->getElementsByTagName(‘channel’)->item(0)->appendChild($item); 6KQLinux联盟
   6KQLinux联盟
   //从DOM中删除节点 6KQLinux联盟
   //$dom->documentElement->RemoveChild($dom->documentElement->getElementsByTagName("channel")->item(0)); 6KQLinux联盟
   //或者使用xpath查询出节点再删除 6KQLinux联盟
   //$dom->documentElement->RemoveChild($xpath->query("/rss/channel")->item(0)); 6KQLinux联盟
   //$dom->save("newfile.xml"); 6KQLinux联盟
   6KQLinux联盟
   //从DOM中修改节点数据 6KQLinux联盟
   //修改第一个title的文件 6KQLinux联盟
   //这个地方比较笨,新创建一个节点,然后替换旧的节点。如果哪位朋友有其他好的方法请一定要告诉我 6KQLinux联盟
   $firstTitle = $xpath->query("/rss/channel/item/title")->item(0); 6KQLinux联盟
   $newTitle = $dom->createElement("title"); 6KQLinux联盟
   $newTitle->appendChild(new DOMText("This’s the new title text!!!")); 6KQLinux联盟
   $firstTitle->parentNode->replaceChild($newTitle, $firstTitle); 6KQLinux联盟
   //修改属性 6KQLinux联盟
   //$firstTitle = $xpath->query("/rss/channel/item/title")->item(0); 6KQLinux联盟
   //$firstTitle->setAttribute("orderby", "4"); 6KQLinux联盟
   $dom->save("newfile.xml"); 6KQLinux联盟
   6KQLinux联盟
   echo "<hr/><a href="newfile.xml">查看newfile.xml</a>"; 6KQLinux联盟
   6KQLinux联盟
   //下面的代码获得并解析php.net的首页,将返第一个title元素的内容。 6KQLinux联盟
   /* 6KQLinux联盟
   $dom->loadHTMLFile("http://www.php.net/"); 6KQLinux联盟
   $title = $dom->getElementsByTagName("title"); 6KQLinux联盟
   print $title->item(0)->textContent; 6KQLinux联盟
   */ 6KQLinux联盟
   ?> 6KQLinux联盟
   6KQLinux联盟
   下面是test.xml文件代码: 6KQLinux联盟
   6KQLinux联盟
   <?xml version="1.0" encoding="gb2312"?> 6KQLinux联盟
   <rss version="2.0"> 6KQLinux联盟
   <channel> 6KQLinux联盟
   <title>javascript</title> 6KQLinux联盟
   <link>http://blog.csdn.net/zhongmao/category/29515.aspx</link> 6KQLinux联盟
   <description>javascript</description> 6KQLinux联盟
   <language>zh-chs</language> 6KQLinux联盟
   <generator>.text version 0.958.2004.2001</generator> 6KQLinux联盟
   <item> 6KQLinux联盟
   <creator>zhongmao</creator> 6KQLinux联盟
   <title orderby="1">out put Excel used javascript</title> 6KQLinux联盟
   <link>http://blog.csdn.net/zhongmao/archive/2004/09/15/105385.aspx</link> 6KQLinux联盟
   <pubdate>wed, 15 sep 2004 13:32:00 gmt</pubdate> 6KQLinux联盟
   <guid>http://blog.csdn.net/zhongmao/archive/2004/09/15/105385.aspx</guid> 6KQLinux联盟
   <comment>http://blog.csdn.net/zhongmao/comments/105385.aspx</comment> 6KQLinux联盟
   <comments>http://blog.csdn.net/zhongmao/archive/2004/09/15/105385.aspx#feedback6KQLinux联盟
</comments> 6KQLinux联盟
   <comments>2</comments> 6KQLinux联盟
   <commentrss>http://blog.csdn.net/zhongmao/comments/commentrss/105385.aspx6KQLinux联盟
</commentrss> 6KQLinux联盟
   <ping>http://blog.csdn.net/zhongmao/services/trackbacks/105385.aspx</ping> 6KQLinux联盟
   <description>test description</description> 6KQLinux联盟
   </item> 6KQLinux联盟
   <item> 6KQLinux联盟
   <creator>zhongmao</creator> 6KQLinux联盟
   <title orderby="2">out put word used javascript</title> 6KQLinux联盟
   <link>http://blog.csdn.net/zhongmao/archive/2004/08/06/67161.aspx</link> 6KQLinux联盟
   <pubdate>fri, 06 aug 2004 16:33:00 gmt</pubdate> 6KQLinux联盟
   <guid>http://blog.csdn.net/zhongmao/archive/2004/08/06/67161.aspx</guid> 6KQLinux联盟
   <comment>http://blog.csdn.net/zhongmao/comments/67161.aspx</comment> 6KQLinux联盟
   <comments>http://blog.csdn.net/zhongmao/archive/2004/08/06/67161.aspx#feedback6KQLinux联盟
</comments> 6KQLinux联盟
   <comments>0</comments> 6KQLinux联盟
   <commentrss>http://blog.csdn.net/zhongmao/comments/commentrss/67161.aspx6KQLinux联盟
</commentrss> 6KQLinux联盟
   <ping>http://blog.csdn.net/zhongmao/services/trackbacks/67161.aspx</ping> 6KQLinux联盟
   <description>test word description</description> 6KQLinux联盟
   </item> 6KQLinux联盟
   <item> 6KQLinux联盟
   <creator>zhongmao</creator> 6KQLinux联盟
   <title orderby="3">xmlhttp</title> 6KQLinux联盟
   <link>http://blog.csdn.net/zhongmao/archive/2004/08/02/58417.aspx</link> 6KQLinux联盟
   <pubdate>mon, 02 aug 2004 10:11:00 gmt</pubdate> 6KQLinux联盟
   <guid>http://blog.csdn.net/zhongmao/archive/2004/08/02/58417.aspx</guid> 6KQLinux联盟
   <comment>http://blog.csdn.net/zhongmao/comments/58417.aspx</comment> 6KQLinux联盟
   <comments>http://blog.csdn.net/zhongmao/archive/2004/08/02/58417.aspx#feedback6KQLinux联盟
</comments> 6KQLinux联盟
   <comments>0</comments> 6KQLinux联盟
   <commentrss>http://blog.csdn.net/zhongmao/comments/commentrss6KQLinux联盟
/58417.aspx</commentrss> 6KQLinux联盟
   <ping>http://blog.csdn.net/zhongmao/services/trackbacks/58417.aspx</ping> 6KQLinux联盟
   <description>xmlhttpaaa asd bb cc dd</description> 6KQLinux联盟
   </item> 6KQLinux联盟
   </channel> 6KQLinux联盟
   </rss>

如何使用PHP DOM创建动态XML文件

当处理基于XML应用程序时,开发者经常需要建立XML编码数据结构。例如,Web中基于用户输入的XML状态模板,服务器请求XML语句,以及基于运行时间参数的客户响应。

尽管XML数据结构的构建比较费时,但如果使用成熟的PHP DOM应用程序接口,一切都会变得简单明了。本文将向你介绍PHP DOM应用程序接口的主要功能,演示如何生成一个正确的XML完整文件并将其保存到磁盘中。

创建文档类型声明

一般而言,XML声明放在文档顶部。在PHP中声明十分简单:只需实例化一个DOM文档类的对象并赋予它一个版本号。查看程序清单A

程序清单 A

<?php
// create doctype
$dom = new DOMDocument("1.0");

// display document in browser as plain text
// display document in browser as plain text
// for readability purposes
header("Content-Type: text/plain");

// save and display tree
echo $dom->saveXML();
?>

请注意DOM文档对象的saveXML()方法。稍后我再详细介绍这一方法,现在你只需要简单认识到它用于输出XML文档的当前快照到一个文件或浏览器。在本例,为增强可读性,我已经将ASCII码文本直接输出至浏览器。在实际应用中,可将以text/XML头文件发送到浏览器。

如在浏览器中查看输出,你可看到如下代码:

<?xml version="1.0"?>

添加元素和文本节点

XML真正强大的功能是来自其元素与封装的内容。幸运的是,一旦你初始化DOM文档,很多操作变得很简单。此过程包含如下两步骤:

  • 对想添加的每一元素或文本节点,通过元素名或文本内容调用DOM文档对象的createElement()或createTextNode()方法。这将创建对应于元素或文本节点的新对象。
  • 通过调用节点的appendChild()方法,并把其传递给上一步中创建的对象,并在XML文档树中将元素或文本节点添加到父节点。

以下范例将清楚地演示这2步骤,请查看程序清单B

程序清单 B

<?php
// create doctype
$dom = new DOMDocument("1.0");

// display document in browser as plain text
// for readability purposes
header("Content-Type: text/plain");

// create root element
$root = $dom->createElement("toppings");
$dom->appendChild($root);

// create child element
$item = $dom->createElement("item");
$root->appendChild($item);

// create text node
$text = $dom->createTextNode("pepperoni");
$item->appendChild($text);

// save and display tree
echo $dom->saveXML();
?>

这 里,我首先创建一个名字为<toppings>的根元素,并使它归于XML头文件中。然后,我建立名为<item>的元素并使它 归于根元素。最后,我又创建一个值为“pepperoni”的文本节点并使它归于<item>元素。最终结果如下:

<?xml version="1.0"?>
<toppings>
?<item>pepperoni</item>
</toppings>

如果你想添加另外一个topping,只需创建另外一个<item>并添加不同的内容,如程序清单C所示。

程序清单C

<?php
// create doctype
$dom = new DOMDocument("1.0");

// display document in browser as plain text
// for readability purposes
header("Content-Type: text/plain");

// create root element
$root = $dom->createElement("toppings");
$dom->appendChild($root);

// create child element
$item = $dom->createElement("item");
$root->appendChild($item);

// create text node
$text = $dom->createTextNode("pepperoni");
$item->appendChild($text);

// create child element
$item = $dom->createElement("item");
$root->appendChild($item);

// create another text node
$text = $dom->createTextNode("tomato");
$item->appendChild($text);

// save and display tree
echo $dom->saveXML();
?>

以下是执行程序清单C后的输出:

<?xml version="1.0"?>
<toppings>
?<item>pepperoni</item>
?<item>tomato</item>
</toppings>

添加属性

通过使用属性,你也可以添加适合的信息到元素。对于PHP DOM API,添加属性需要两步:首先用DOM文档对象的createAttribute()方法创建拥有此属性名字的节点,然后将文档节点添加到拥有属性值的属性节点。详见程序清单D。

程序清单 D

<?php
// create doctype
$dom = new DOMDocument("1.0");

// display document in browser as plain text
// for readability purposes
header("Content-Type: text/plain");
// create root element
$root = $dom->createElement("toppings");
$dom->appendChild($root);

// create child element
$item = $dom->createElement("item");
$root->appendChild($item);

// create text node
$text = $dom->createTextNode("pepperoni");
$item->appendChild($text);

// create attribute node
$price = $dom->createAttribute("price");
$item->appendChild($price);

// create attribute value node
$priceValue = $dom->createTextNode("4");
$price->appendChild($priceValue);

// save and display tree
echo $dom->saveXML();
?>

输出如下所示:

<?xml version="1.0"?>
<toppings>
?<item price="4">pepperoni</item>
</toppings>

添加CDATA模块和过程向导

虽然不经常使用CDATA模块和过程向导,但是通过调用DOM文档对象的createCDATASection()createProcessingInstruction()方法 PHP API 也能很好地支持CDATA和过程向导,请见程序清单E。

程序清单 E

<?php
// create doctype
// create doctype
$dom = new DOMDocument("1.0");

// display document in browser as plain text
// for readability purposes
header("Content-Type: text/plain");

// create root element
$root = $dom->createElement("toppings");
$dom->appendChild($root);

// create child element
$item = $dom->createElement("item");
$root->appendChild($item);

// create text node
$text = $dom->createTextNode("pepperoni");
$item->appendChild($text);

// create attribute node
$price = $dom->createAttribute("price");
$item->appendChild($price);

// create attribute value node
$priceValue = $dom->createTextNode("4");
$price->appendChild($priceValue);

// create CDATA section
$cdata = $dom->createCDATASection(" Customer requests that pizza be sliced into 16 square pieces ");
$root->appendChild($cdata);

// create PI
$pi = $dom->createProcessingInstruction("pizza", "bake()");
$root->appendChild($pi);

// save and display tree
echo $dom->saveXML();
?>

输出如下所示:

<?xml version="1.0"?>
<toppings>
<item price="4">pepperoni</item>
<![CDATA[
      Customer requests that pizza be sliced into 16 square pieces
]]>
<?pizza bake()?>
</toppings>

保存结果

一旦已经实现你的目标,就可以将结果保存在一个文件或存储于PHP的变量。通过调用带有文件名的save()方法可以将结果保存在文件中,而通过调用saveXML()方法可存储于PHP的变量。请参考以下实例(程序清单F):

程序清单 F

<?php
// create doctype
$dom = new DOMDocument("1.0");

// create root element
$root = $dom->createElement("toppings");
$dom->appendChild($root);
$dom->formatOutput=true;

// create child element
$item = $dom->createElement("item");
$root->appendChild($item);

// create text node
$text = $dom->createTextNode("pepperoni");
$item->appendChild($text);

// create attribute node
$price = $dom->createAttribute("price");
$item->appendChild($price);

// create attribute value node
$priceValue = $dom->createTextNode("4");
$price->appendChild($priceValue);

// create CDATA section
$cdata = $dom->createCDATASection(" Customer requests that pizza be

sliced into 16 square pieces ");
$root->appendChild($cdata);

// create PI
$pi = $dom->createProcessingInstruction("pizza", "bake()");
$root->appendChild($pi);

// save tree to file
$dom->save("order.xml");

// save tree to string
$order = $dom->save("order.xml");
?>

希望你能在本文发现有用的东西,并在以后使用XML时应用到这些方法。

用 PHP 读取和编写 XML DOM

Jack Herrington (), 高级软件工程师, "Code Generation Network"

2006 年 2 月 06 日

有许多技术可用于用 PHP 读取和编写 XML。本文提供了三种方法读取 XML:使用 DOM 库、使用 SAX 解析器和使用正则表达式。还介绍了使用 DOM 和 PHP 文本模板编写 XML。

用 PHP 读取和编写可扩展标记语言(XML)看起来可能有点恐怖。实际上,XML 和它的所有相关技术可能是恐怖的,但是用 PHP 读取和编写 XML 不一定是项恐怖的任务。首先,需要学习一点关于 XML 的知识 —— 它是什么,用它做什么。然后,需要学习如何用 PHP 读取和编写 XML,而有许多种方式可以做这件事。

本文提供了 XML 的简短入门,然后解释如何用 PHP 读取和编写 XML。

什么是 XML?

XML 是一种数据存储格式。它没有定义保存什么数据,也没有定义数据的格式。XML 只是定义了标记和这些标记的属性。格式良好的 XML 标记看起来像这样:

<name>Jack Herrington</name>

这个 <name> 标记包含一些文本:Jack Herrington。

不包含文本的 XML 标记看起来像这样:

<powerUp />

用 XML 对某件事进行编写的方式不止一种。例如,这个标记形成的输出与前一个标记相同:

<powerUp></powerUp>

也可以向 XML 标记添加属性。例如,这个 <name> 标记包含 firstlast 属性:

<name first="Jack" last="Herrington" />

也可以用 XML 对特殊字符进行编码。例如,& 符号可以像这样编码:

&

包含标记和属性的 XML 文件如果像示例一样格式化,就是格式良好的,这意味着标记是对称的,字符的编码正确。清单 1 是一份格式良好的 XML 的示例。

清单 1. XML 图书列表示例
<books>
<book>
<author>Jack Herrington</author>
<title>PHP Hacks</title>
<publisher>O’Reilly</publisher>
</book>
<book>
<author>Jack Herrington</author>
<title>Podcasting Hacks</title>
<publisher>O’Reilly</publisher>
</book>
</books>

清单 1 中的 XML 包含一个图书列表。父标记 <books> 包含一组 <book> 标记,每个 <book> 标记又包含 <author><title><publisher> 标记。

当 XML 文档的标记结构和内容得到外部模式文件的验证后,XML 文档就是正确的。模式文件可以用不同的格式指定。对于本文来说,所需要的只是格式良好的 XML。

如果觉得 XML 看起来很像超文本标记语言(HTML),那么就对了。XML 和 HTML 都是基于标记的语言,它们有许多相似之处。但是,要着重指出的是:虽然 XML 文档可能是格式良好的 HTML,但不是所有的 HTML 文档都是格式良好的 XML。换行标记(br)是 XML 和 HTML 之间区别的一个好例子。这个换行标记是格式良好的 HTML,但不是格式良好的 XML:

<p>This is a paragraph<br>
With a line break</p>

这个换行标记是格式良好的 XML 和 HTML:

<p>This is a paragraph<br />
With a line break</p>

如果要把 HTML 编写成同样是格式良好的 XML,请遵循 W3C 委员会的可扩展超文本标记语言(XHTML)标准(参见 参考资料)。所有现代的浏览器都能呈现 XHTML。而且,还可以用 XML 工具读取 XHTML 并找出文档中的数据,这比解析 HTML 容易得多。



回页首

使用 DOM 库读取 XML

读取格式良好的 XML 文件最容易的方式是使用编译成某些 PHP 安装的文档对象模型 (DOM)库。DOM 库把整个 XML 文档读入内存,并用节点树表示它,如图 1 所示。

图 1. 图书 XML 的 XML DOM 树
图书 XML 的 XML DOM 树

树顶部的 books 节点有两个 book 子标记。在每本书中,有 authorpublishertitle 几个节点。authorpublishertitle 节点分别有包含文本的文本子节点。

读取图书 XML 文件并用 DOM 显示内容的代码如清单 2 所示。

清单 2. 用 DOM 读取图书 XML
<?php
$doc = new DOMDocument();
$doc->load( ‘books.xml’ );

$books = $doc->getElementsByTagName( "book" );
foreach( $books as $book )
{
$authors = $book->getElementsByTagName( "author" );
$author = $authors->item(0)->nodeValue;

$publishers = $book->getElementsByTagName( "publisher" );
$publisher = $publishers->item(0)->nodeValue;

$titles = $book->getElementsByTagName( "title" );
$title = $titles->item(0)->nodeValue;

echo "$title – $author – $publishern";
}
?>

脚本首先创建一个 new DOMdocument 对象,用 load 方法把图书 XML 装入这个对象。之后,脚本用 getElementsByName 方法得到指定名称下的所有元素的列表。

book 节点的循环中,脚本用 getElementsByName 方法获得 authorpublishertitle 标记的 nodeValuenodeValue 是节点中的文本。脚本然后显示这些值。

可以在命令行上像这样运行 PHP 脚本:

% php e1.php
PHP Hacks - Jack Herrington - O'Reilly
Podcasting Hacks - Jack Herrington - O'Reilly
%

可以看到,每个图书块输出一行。这是一个良好的开始。但是,如果不能访问 XML DOM 库该怎么办?



回页首

用 SAX 解析器读取 XML

读取 XML 的另一种方法是使用 XML Simple API(SAX)解析器。PHP 的大多数安装都包含 SAX 解析器。SAX 解析器运行在回调模型上。每次打开或关闭一个标记时,或者每次解析器看到文本时,就用节点或文本的信息回调用户定义的函数。

SAX 解析器的优点是,它是真正轻量级的。解析器不会在内存中长期保持内容,所以可以用于非常巨大的文件。缺点是编写 SAX 解析器回调是件非常麻烦的事。清单 3 显示了使用 SAX 读取图书 XML 文件并显示内容的代码。

清单 3. 用 SAX 解析器读取图书 XML
<?php
$g_books = array();
$g_elem = null;

function startElement( $parser, $name, $attrs )
{
global $g_books, $g_elem;
if ( $name == ‘BOOK’ ) $g_books []= array();
$g_elem = $name;
}

function endElement( $parser, $name )
{
global $g_elem;
$g_elem = null;
}

function textData( $parser, $text )
{
global $g_books, $g_elem;
if ( $g_elem == ‘AUTHOR’ ||
$g_elem == ‘PUBLISHER’ ||
$g_elem == ‘TITLE’ )
{
$g_books[ count( $g_books ) – 1 ][ $g_elem ] = $text;
}
}

$parser = xml_parser_create();

xml_set_element_handler( $parser, "startElement", "endElement" );
xml_set_character_data_handler( $parser, "textData" );

$f = fopen( ‘books.xml’, ‘r’ );

while( $data = fread( $f, 4096 ) )
{
xml_parse( $parser, $data );
}

xml_parser_free( $parser );

foreach( $g_books as $book )
{
echo $book[‘TITLE’]." – ".$book[‘AUTHOR’]." – ";
echo $book[‘PUBLISHER’]."n";
}
?>

脚本首先设置 g_books 数组,它在内存中容纳所有图书和图书信息,g_elem 变量保存脚本目前正在处理的标记的名称。然后脚本定义回调函数。在这个示例中,回调函数是 startElementendElementtextData。在打开和关闭标记的时候,分别调用 startElementendElement 函数。在开始和结束标记之间的文本上面,调用 textData

在这个示例中,startElement 标记查找 book 标记,在 book 数组中开始一个新元素。然后,textData 函数查看当前元素,看它是不是 publishertitleauthor 标记。如果是,函数就把当前文本放入当前图书。

为了让解析继续,脚本用 xml_parser_create 函数创建解析器。然后,设置回调句柄。之后,脚本读取文件并把文件的大块内容发送到解析器。在文件读取之后,xml_parser_free 函数删除解析器。脚本的末尾输出 g_books 数组的内容。

可以看到,这比编写 DOM 的同样功能要困难得多。如果没有 DOM 库也没有 SAX 库该怎么办?还有替代方案么?



回页首

用正则表达式解析 XML

可以肯定,即使提到这个方法,有些工程师也会批评我,但是确实可以用正则表达式解析 XML。清单 4 显示了使用 preg_ 函数读取图书文件的示例。

清单 4. 用正则表达式读取 XML
<?php
$xml = "";
$f = fopen( ‘books.xml’, ‘r’ );
while( $data = fread( $f, 4096 ) ) { $xml .= $data; }
fclose( $f );

preg_match_all( "/<book>(.*?)</book>/s",
$xml, $bookblocks );

foreach( $bookblocks[1] as $block )
{
preg_match_all( "/<author>(.*?)</author>/",
$block, $author );
preg_match_all( "/<title>(.*?)</title>/",
$block, $title );
preg_match_all( "/<publisher>(.*?)</publisher>/",
$block, $publisher );
echo( $title[1][0]." – ".$author[1][0]." – ".
$publisher[1][0]."n" );
}
?>

请注意这个代码有多短。开始时,它把文件读进一个大的字符串。然后用一个 regex 函数读取每个图书项目。最后用 foreach 循环,在每个图书块间循环,并提取出 author、title 和 publisher。

那么,缺陷在哪呢?使用正则表达式代码读取 XML 的问题是,它并没先进行检查,确保 XML 的格式良好。这意味着在读取之前,无法知道 XML 是否格式良好。而且,有些格式正确的 XML 可能与正则表达式不匹配,所以日后必须修改它们。

我从不建议使用正则表达式读取 XML,但是有时它是兼容性最好的方式,因为正则表达式函数总是可用的。不要用正则表达式读取直接来自用户的 XML,因为无法控制这类 XML 的格式或结构。应当一直用 DOM 库或 SAX 解析器读取来自用户的 XML。



回页首

用 DOM 编写 XML

读取 XML 只是公式的一部分。该怎样编写 XML 呢?编写 XML 最好的方式就是用 DOM。清单 5 显示了 DOM 构建图书 XML 文件的方式。

清单 5. 用 DOM 编写图书 XML
<?php
$books = array();
$books [] = array(
‘title’ => ‘PHP Hacks’,
‘author’ => ‘Jack Herrington’,
‘publisher’ => "O’Reilly"
);
$books [] = array(
‘title’ => ‘Podcasting Hacks’,
‘author’ => ‘Jack Herrington’,
‘publisher’ => "O’Reilly"
);

$doc = new DOMDocument();
$doc->formatOutput = true;

$r = $doc->createElement( "books" );
$doc->appendChild( $r );

foreach( $books as $book )
{
$b = $doc->createElement( "book" );

$author = $doc->createElement( "author" );
$author->appendChild(
$doc->createTextNode( $book[‘author’] )
);
$b->appendChild( $author );

$title = $doc->createElement( "title" );
$title->appendChild(
$doc->createTextNode( $book[‘title’] )
);
$b->appendChild( $title );

$publisher = $doc->createElement( "publisher" );
$publisher->appendChild(
$doc->createTextNode( $book[‘publisher’] )
);
$b->appendChild( $publisher );

$r->appendChild( $b );
}

echo $doc->saveXML();
?>

在脚本的顶部,用一些示例图书装入了 books 数组。这个数据可以来自用户也可以来自数据库。

示例图书装入之后,脚本创建一个 new DOMDocument,并把根节点 books 添加到它。然后脚本为每本书的 author、title 和 publisher 创建节点,并为每个节点添加文本节点。每个 book 节点的最后一步是重新把它添加到根节点 books

脚本的末尾用 saveXML 方法把 XML 输出到控制台。(也可以用 save 方法创建一个 XML 文件。)脚本的输出如清单 6 所示。

清单 6. DOM 构建脚本的输出
% php e4.php
<?xml version="1.0"?>
<books>
<book>
<author>Jack Herrington</author>
<title>PHP Hacks</title>
<publisher>O’Reilly</publisher>
</book>
<book>
<author>Jack Herrington</author>
<title>Podcasting Hacks</title>
<publisher>O’Reilly</publisher>
</book>
</books>
%

使用 DOM 的真正价值在于它创建的 XML 总是格式正确的。但是如果不能用 DOM 创建 XML 时该怎么办?



回页首

用 PHP 编写 XML

如果 DOM 不可用,可以用 PHP 的文本模板编写 XML。清单 7 显示了 PHP 如何构建图书 XML 文件。

清单 7. 用 PHP 编写图书 XML
<?php
$books = array();
$books [] = array(
‘title’ => ‘PHP Hacks’,
‘author’ => ‘Jack Herrington’,
‘publisher’ => "O’Reilly"
);
$books [] = array(
‘title’ => ‘Podcasting Hacks’,
‘author’ => ‘Jack Herrington’,
‘publisher’ => "O’Reilly"
);
?>
<books>
<?php

foreach( $books as $book )
{
?>
<book>
<title><?php echo( $book[‘title’] ); ?></title>
<author><?php echo( $book[‘author’] ); ?>
</author>
<publisher><?php echo( $book[‘publisher’] ); ?>
</publisher>
</book>
<?php
}
?>
</books>

脚本的顶部与 DOM 脚本类似。脚本的底部打开 books 标记,然后在每个图书中迭代,创建 book 标记和所有的内部 titleauthorpublisher 标记。

这种方法的问题是对实体进行编码。为了确保实体编码正确,必须在每个项目上调用 htmlentities 函数,如清单 8 所示。

清单 8. 使用 htmlentities 函数对实体编码

<books>
<?php

foreach( $books as $book )
{
$title = htmlentities( $book[‘title’], ENT_QUOTES );
$author = htmlentities( $book[‘author’], ENT_QUOTES );
$publisher = htmlentities( $book[‘publisher’], ENT_QUOTES );
?>
<book>
<title><?php echo( $title ); ?></title>
<author><?php echo( $author ); ?> </author>
<publisher><?php echo( $publisher ); ?>
</publisher>
</book>
<?php
}
?>
</books>

这就是用基本的 PHP 编写 XML 的烦人之处。您以为自己创建了完美的 XML,但是在试图使用数据的时候,马上就会发现某些元素的编码不正确。



回页首

结束语

XML 周围总有许多夸大之处和混淆之处。但是,并不像您想像的那么难 —— 特别是在 PHP 这样优秀的语言中。在理解并正确地实现了 XML 之后,就会发现有许多强大的工具可以使用。XPath 和 XSLT 就是这样两个值得研究的工具。

参考资料

学习

获得产品和技术

  • 请访问 PHP.net,了解关于 PHP 的最新新闻、找到下载,并向其他用户学习。
  • 了解 Expat XML Parser,这个解析器用来向 PHP 提供 SAX 解析器功能。
  • 利用 IBM 试用软件 改造您的下一个开放源码开发项目,可以下载也可以通过 DVD 得到。

讨论

关于作者

Jack D. Herrington 是有 20 多年经验的高级软件工程师。他是三本书的作者:Code Generation in ActionPodcasting Hacks 和即将发表的 PHP Hacks。他还撰写了 30 多篇文章。