Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2780941
  • 博文数量: 77
  • 博客积分: 10204
  • 博客等级: 上将
  • 技术积分: 5035
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-05 22:13
文章分类
文章存档

2013年(1)

2010年(1)

2009年(17)

2008年(58)

我的朋友

分类:

2008-03-31 17:03:40

第5章 PHP与XML

译者:Jimmy

转载请注明出处%e7%ac%ac5%e7%ab%a0-php%e4%b8%8exml/

PHP的最新版本,PHP5,包括了PHP的一些新特性和一些函数的增强。PHP5引进了一个新的对象模型,异常和新的数据库支持,比如MySQLi和SQLite,而且在XML和Web services领域也有很大的跨越。本章将介绍基于XML的新扩展,它们的组成库和PHP5 XML扩展的一些通用的基本方法。

介绍PHP5中的XML

PHP4中的原生XML支持被限制在一些特定的基本技术范围内。它的XML扩展支持SAX,一个提供树形支持(tree support)和一些XSLT支持的domxml扩展。考虑到Web Services和数据交换,wddx扩展支持分布式数据交换技术(distributed data exchange),XMLRPC支持基于XML的远程控制调用(underlying)。尽管看起来这是一些非常不错的技术,但是它们存在一个非常基本的问题,就是它们各个都使用属于它们自己的扩展。这些扩展不能一起工作,而且为了使用这些扩展,你必须安装所有必需的库。

PHP4中XML的这些毛病让那些使用它们的人感觉非常不爽。所有的这些XML技术都是可以使用的,但是它们就是不能在一起工作。因此,早在PHP5开发的初级阶段,一场关于PHP5中XML未来的最终形态的讨论就开始了。开发人员决定重写XML-base扩展来尽可能提供最好的函数和灵活性。

PHP5中的libxml2

依赖于XML扩展的最核心的库是libxml2,可以在找到。这个库支持许多与XML相关的标准,包括XML,Namespaces,XML Schemas,RelaxNG,XPath和XInclude说明。选择它的原因是它对XML的广泛支持,这意味着一些额外的技术可以被应用到PHP当中,而且它是一个最快的解析器;另外还因为它的活跃性和使用的广泛性。它的兄弟,同样依赖于libxml2扩展的libxslt库在PHP5中被用来处理XSL,也可以在找到。

这两个扩展非常活跃,开发人员通过修改bug和增强特性来发展它们。为了尽可能提供最好的XML支持,有时候在制作PHP安装包时必须加入这两个库的最新版本。目前这两个扩展在PHP5.0.x中要求的最低版本是libxml2 2.5.10和libxslt 1.0.18,PHP5.1.x中的最低要求是libxml2.6.11和libxslt 1.0.18。尽管确定了最低要求标准,但是让你的类库保持到最新状态不失为一种好方法。新版本中修复的bug会增强和保证PHP扩展中对XML的支持。有时候最新版本的libxml2还提供新的函数。

TIP
将libxml2和libxslt两个库更新到最新版本保证了你及时修复了最新的bug,同时,你还可以拥有一些旧版本中没有的函数。要记住更新到最新的版本也意味着你的程序可能不会像旧版本之前一样如实运行。这两个库都遵循XML规范,为了符合这些规范,一些新的版本可能会产生意想不到的输出。虽然如此,还是要更新你的程序来适应这些新变化而不是为了避免错误而依赖于旧的版本。

核心XML扩展

一些PHP可以使用的XML扩展和包将本书的最后来介绍,如果你是个PEAR爱好者,请记住,你并没有被我忽略,我将在第13章用整章的篇幅来介绍PEAR与XML。在本章,我主要介绍PHP5捆绑的一些扩展。

树形解析器(Tree-Based Parsers)

树形解析器允许你建立或加载已有的XML文件,这样你就可以遍历或修改它们。为了做到这一点,创建或加载的整个XML文档将以树形的形式加载到内存中。假定整个文件必须驻留在内存中,在使用这些技术的时候你必须考虑内存的参数。这些解析器在刚加载的时候会有点慢,一旦加载到内存后,这些解析器将比其他类型的解析器来得快。

在PHP4中,domxml是唯一的一个原生的树形解析器。PHP5引进了新的解析器,DOM和SimpleXML。如果你对它们不熟悉,你必须知道为什么你需要它们。在阅读第6,7和11章后你就会明白这一切的。

SimpleXML扩展

PHP5中提供的SimpleXML是一个极其简单的轻量工具,可以用它来处理XML文档。与DOM扩展相比较,SimpleXML是一个容易学习的API,因为你可以将整个文档树视为一个对象,对象跟元素节点是同义的。你可以像使用一个数组一样来使用对象属性。在一定的范围内,SimpleXML也提供了内容的编辑。你可以在第7章进一步获得关于SimpleXML的信息,还可以看到更详细的介绍和使用范例。

DOM扩展

PHP5中的DOM扩展是用来代替PHP4中唯一树形解析器扩展domxml的。DOM扩展用来解决domxml中的一些毛病,它同样附属于W3C DOM 规范。与SimpleXML不同的是它拥有一个庞大和复杂的API。DOM扩展允许你访问所有类型的节点,允许你创建和修改复杂的文档,而且提供更高级的遍历函数。如果你在其它语言中使用过DOM解析器,你会发现在PHP中使用它是如此的容易。下一章将详细介绍这个扩展。

流解析器(Streaming Parsers)

与树形解析器不同,流解析器并没有将整个文档载入内存,一次只处理一小段的文档,所以将降低内存的使用率。PHP5提供了分别利用xml扩展和XMLReader扩展的push parser。这些解析器不能用来编辑文档,而且没有遍历的能力(offer little to no navigational capabilities),因为它们是一个只向前(forward-only)的流。The minor exception to this is XMLReader.

XML扩展

Xml扩展以一个类似基于SAX的工具从PHP4开始出现。在PHP5中,libxml兼容层被作为一个默认的类库。SAX提供基于事件的解析器。Handlers被指派给事件,当开始或结束遇到一个元素时,数据将被发送到函数去处理。这就是所谓的push parser,因为你不能控制数据被发送到你的函数。当开始解析时,XML文档的阅读也开始了。当一个事件被触发的时候,你的handler将处理那些不论你兴趣与否的实际数据。这将持续到你停止解析,或发生一个致命的错误,或已经到达了文档的末尾。将在第8章介绍xml扩展的API,并提供一些实例。

第6章 文档对象模型(DOM)

DOM是用来访问和修改文档的一系列接口。虽然它是一个标准,实际上它被分成各种不同的规范。W3C管理着这些规范 。DOM的核心函数被分成标准1,标准2,标准3;每个标准都提供了新增的函数,相应的API数量也增加了。

理解DOM树形结构

在DOM中,文档被视为拥有各个节点的树来处理。这意味这整个文档都将被加载到或内置到内存中,文档树将被分解成从一个节点派生出来的一个个小的单元。这些节点是主要的数据类型,其它的节点类型都从它派生出来。下面是DOM里面的节点类型:

• Attr: Attribute node
• CDATASection: CDATA section node
• Comment: Comment node
• DocumentFragment: Document fragment node
• Document: Document node
• DocumentType: Document type node
• Element: Element node
• Entity: Entity node
• EntityReference: Entity reference node
• Notation: Notation node
• ProcessingInstruction: PI node
• Text: Text node

每一个节点类型都对应于一个DOM对象。已经存在的对象不是从节点对象继承过来的,比如NodeList,NameNodeMap,DOMImplementation,DOMException和CharacterData。其中CharacterData是一个特别的对象,它实际上是从一个节点对象继承的,但缺不是一个直接的DOM对象,它提供一些文本节点继承的额外函数。为了补充签名提到的那些对象和接口,DOM提供了一些其他的接口,特别是核心标准3,但它们不能在PHP5中的DOM实现。因此不在讨论的范围内。

被视为树形的文档可以从各个方向被遍历。每个节点类型都可以被访问,而且节点类型决定了它可以使用哪些函数。因为每个节点类型都是从基本的Node接口继承,所有从基本节点派生出来的函数都是可以使用的,当然有些特定的函数只能用在一些特定的节点类型,除了一些特定的函数应用到一些特定的节点。例如,Node接口包括一个返回节点值的只读属性nodeValue。文档和实体引用节点是两个没有值的节点类型,它们会返回NULL值给nodeValue属性。

一个简单的XML文档:

  1. xml version="1.0"?>
  2. <root>
  3. <child att1="Att1 value">Child Contentschild>
  4. root>

一旦被加载到DOM扩展中,XML就会以DOM对象的形式呈现出来。如图6-1所示。

如果没有特别指明,换行符和制表符等一些空白也会在DOM树中以节点形式存在。这一点很重要,因为这将影响到树的遍历。例如,根节点包含有一些文本类型的子节点。XML和DOM扩展的初学者经常没有注意这一点。

节点对象

节点对象组成了XML文档的实际结构。这些对象使得XML结构可以被遍历和操作。比如文档中的一个元素可以通过Element接口来访问。这个接口提供了可以读写和移动它下属的节点的功能。你也可以使用这个接口来创建新的节点并把它们插入到文档。在读了“使用DOM扩展”那一节后你对这些概念就会更加清楚了。

其他对象

DOM提供了一些与节点有关但是不是与某一特定节点类型相关的对象接口,这些对象有CharacterData,NodeList,NameNodeMap,DOMImplementation和DOMException。

CharacterData

CharacterData是从Node接口继承来的,但是不直接跟文档中的某一特定类型一致。这个接口实际上被看成是文本和comment节点的基本类型,这样可以为处理文本内容提供了额外的函数。

NodeList

NodeList是一个可以用从0开始的索引来访问的顺序集。这种对象一般是从可以同时返回多个单节点的DOM方法返回过来的。这种类型的对象是动态的,对XML文档树的任何操作将直接反映到这种对象上。比如对文档子节点的删除、增加、修改或移动等都会使NodeList对象的索引和内容发生变化。
NameNodeMap
NameNodeMap与NodeList相似,它除了可以使用数字索引访问其结果集外还可以使用name索引。索引的不同还在与它们的结果集并没有安装特定的顺序排列的,因为它们主要是设计来按照name索引访问的。NameNodeMap结果集也是动态的,跟NodeList的动态一样。

DOMImplementaion
DOMImplementaiond对象是用来处理独立于文档的一组函数,在PHP5中,他主要用来创建DOMDocumentType节点或一个包含DOMDocumentType节点的文档。

使用DOM扩展

理解文档
DOMDocument类是所有使用XML扩展的程序的起始点。它可以用来创建,加载和保存XML文档,它还有一个可以创建各种不同的节点类型的工厂方法。其构造器形式如下:
__construct([string version], [string encoding])

version和encoding两个参数都是可选的,version指XML规范的版本,默认值为1.0,encoding指XML文档的编码。

$dom = new DOMDocument(’1.0′);这行代码相当于创建这样一个XML文档

  1. xml version="1.0"?>

这是一个空的文档,可以利用DOM的API来创建一颗树或从一个XML文档中加载来创建一颗树。创建树的时候要注意,如果数据不是UTF-8编码的在加载前要进行编码转换。如果在要加载前的XML文档中已经声明了编码就不用进行编码转换,因为XML扩展会自动将数据转换。

可以使用load()来从一个资源来加载数据,也可以使用loadXML()从一个字符串来加载。这两个方法都有一个可选的第二参数,也就是解析选项“parse options”,用来指明如何解析树形结构。

  1. $xmldata = 'xml version="1.0"?>
  2. contents
  3. ';
  4. $dom->loadXML($xmldata, LIBXML_NOBLANKS);

根据LIBXML_NOBLANKS参数,loadXML()方法在加载$xmldata对象时会删除其中的空格,换行符和制表符等,加载完后$dom中的内容形式为contents

load()方法用来从一个URI来接着数据,可以使用file和http等协议来加载。

比如:

  1. /* File located in current script directory */
  2. $dom->load('xmldata.xml', LIBXML_NOBLANKS);
  3. /* File loaded using absolute path */
  4. $dom->load('file:///tmp/xmldata.xml', LIBXML_NOBLANKS);
  5. /* File loaded from */
  6. $dom->load('', LIBXML_NOBLANKS);

也可以在没有实例化的情况下静态调用DOMDocument对象来创建一个DOMDocument对象。如

  1. /* Load from string */
  2. $dom = DOMDocument::loadXML('contents');
  3. /* Load from URI */
  4. $dom = DOMDocument::load('xmldata.xml', LIBXML_NOBLANKS);

在PHP5.0中一般没这么使用,因为PHP5.0中的load()没有第二个参数,因此一般使用对象的属性来代替第二个参数。比如:

  1. /* Removing blanks under PHP 5.0 */
  2. $dom = new DOMDocument();
  3. $dom->preserveWhiteSpace = FALSE;
  4. $dom->load('xmldata.xml');

如果同时使用解析选项和对象属性来设定DOM的解析行为,解析选项的优先级会比对象属性高,也就是按照解析选项来设定解析行为。

如果想加载一个HTML文件或HTML字符串,可以分别使用loadHTMLFile()或loadHTML()方法,它们都只提供一个参数。

  1. /* Load the file */
  2. $dom = new DOMDocument();
  3. $dom->loadHTMLFile('');
  4. /* Loading statically */
  5. $dom = DOMDocument::loadHTMLFile('');

要输出一个XML文档,可以使用saveXML()方法和save()方法,它们分别用来输出内容和保存XML到一个文件。saveXML()有一个可选的节点参数,节点参数必须是一个从DOMNode类派生出来的对象,而且必须存在于要输出的文档中,如果没有设定这个参数,将输出整个XML文档。

$output = $dom->saveXML();//输出整个文档
$output = $dom->saveXML($child);/输出文档中的$child指定的节点

save()方法也有一个参数,用来指定要输出的文件名,它会输出整个文档。
$bytes = $dom->save(’output.xml);

这段代码会把XML文档村到一个名为output.xml的文件中。并将写入的文件大小返回给$bytes变量。当运行save()和saveXML() 时,如果加载的XML代码没有任何XML声明,输出的XML中将会自动加上一行XML声明代码
如果在加载代码的时候使用了解析选项LIBXML_NOBLANKS,输出的代码中将不包含任何空格,换行符和制表符,这显然不利于人们阅读,为了格式化输出,可以指定DOMDocument中的一个formatOutput选项为true,例如:

  1. $dom->formatOutput = TRUE;
  2. print $dom->saveXML();

这样就会输出格式良好的XML。
也可以以HTML格式输出一个文档。比如使用saveHTML()或saveHTMLFile()。saveHTML()以字符串形式返回一个输出,saveHTMLFile()将输出写入一个saveHTMLFile()中唯一的一个参数指定的文件中。

  1. /* Serialize document to a string in HTML format */
  2. $html = $htmldoc->saveHTML();
  3. /* Serialize document to file index.html in HTML format */
  4. $bytes = $htmldoc->saveHTMLFile('index.html');

操纵树形结构

DOM扩展拥有比SimpleXML更强大的操纵XML支持。下面以Listing 6-1中所列的XML文档说明。
Listing 6-1. Example Document Using DocBookFormat

  1. xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
  3. "">
  4. <book lang="en">
  5.     <bookinfo>
  6.         <title>DOM in PHP 5title>
  7.         <author>
  8.             <firstname>Robfirstname>
  9.             <surname>Richardssurname>
  10.         author>
  11.         <copyright>
  12.             <year>2005year>
  13.             <holder>Rob Richardsholder>
  14.         copyright>
  15.     bookinfo>
  16.     <preface>
  17.         <title>The DOM Treetitle>
  18.         <para>An example DOM Tree using DocBook.para>
  19.     preface>
  20.     <chapter id="navigation">
  21.         <title>Navigating The Treetitle>
  22.         <para>The document element is accessed from the
  23.         <emphasis>documentElementemphasis> property, which is available from any class
  24.         derived from DOMNodepara>
  25.         <para>The document node is also accessible using the
  26.         <emphasis>ownerDocumentemphasis> property, also derived from the DOMNode
  27.         class.para>
  28.     chapter>
  29. book>

首先要加载这个文档到DOMDocument对象。默认的情况下不会加载DTD,所以文档中的属性id只是一个普通的属性而不是一个ID类型。

  1. $dom = new DOMDocument();
  2. $dom->load('mydocbook.xml');

所有的操纵都必须从DOMDocument对象开始。这些对象没有属性;只有子节点。一个文档也可以拥有一个DTD和任意数量的comment和PI节点。可以用DOMNode类的属性和方法来访问这些节点。

理解文档元素(Document Element)

与文档节点(Document Node)类似,Document Element也是XML的一个焦点。Document Element是文档正文的根,它在一个文档中处于一个固定的位置,是正文和全局访问的入口。从DOMNode类派生的对象都可以访问 documentElement属性,这个属性returns the document element as a DOMElement to also navigate back to the document element.

Listing 6-1中的document element 是book元素。通过DOMDocument对象$dom,可以利用documentElement属性来获取book元素:
$root = $dom->documentElement;
这行代码返回一个DOMElement对象,也就是book元素节点,并赋值给$root。有了document element,接下来就可以操作正文部分了。

节点类型

有时候在返回一个节点时,我们并不知道这个节点的类型,在这种情况下我们就可以使用nodeType属性来返回节点类型。这个属性返回了与内置常数相对应的整形数值:

  1. $type = $root->nodeType;
  2. print $type;

这段代码将返回数字1,对应于XML_ELEMENT_NODE常量。

节点名称

所有节点都有名称,但是有些元素和属性有一些特别的名字。可以用nodeName属性来返回一个节点的名称。

  1. print $dom->nodeName."\n";//返回#document
  2. print $root->nodeName."\n"//返回book

这段代码显示出文档节点与元素节点的节点名称的不同。文档节点$dom返回了#document,而元素节点$root返回元素的标签名book。如果是一个文本节点的话将返回#text。另外还有一些其他的节点类型也有其特别的节点名称,比如entities,entity references,notations,document type definitions和PIs。常用这个属性来获取元素和属性名称。

节点值

属性nodeValue返回了特定节点的内容。节点的值有,属性,CDATA sections,comments,PIs和text。为了方便,PHP5中的 DOM implementation允许使用这个属性来访问元素节点:

  1. print $dom->nodeValue."\n";
  2. print $root->nodeValue."\n";

第一行代码访问了文档节点,而nodeValue属性对文档节点没有意义,因此返回NULL,没有输出。第二行代码中返回了文档元素节点的值(the nodeValueof the document element)。在DOM规范中这个值对文档元素是无效的,PHP5中的DOM扩展为了方便起见返回了这个文档元素范围内的所有文本内容。
上面的代码的输出如下

DOM in PHP 5

Rob
Richards

2005
Rob Richards

第6章 文档对象模型 (二)

访问树中节点

树中节点的访问不限制在往下一级,你可以访问当前节点的兄弟节点,父节点,甚至任何一个可能被访问到的节点。

访问子节点

子节点是当前节点的直接后代。简而言之,所有直接处于当前节点的下一级别的节点都是当前节点的子节点。例如,一个元素节点可能有包含(并不局限于此)注释节点,文本节点和一些其它的元素节点。属性节点只有一个子节点,这个子节点是一个包含属性节点的值的文本节点。文档节点可以包含注释节点,PIs金额一个单独的元素节点来作为子节点。子节点的类型很多时候是由当前节点决定的。可以使用hasChildNodes()方法来检查一个节点是否包含子节点,这个方法将返回一个布尔值来表明该节点是否有子节点。

可以用childNodes属性返回一个包含所有子节点的DOMNoteList对象。DOMNoteList是一个可以循环的对象。可以使用item属性来取得一个DOMNoteList中的一个特定的节点或者利用PHP中的循环函数来访问。比如

  1. if ($root->hasChildNodes()) {
  2.     $children = $root->childNodes;
  3.     foreach($children as $node) {
  4.         print $node->nodeName."\n";
  5.     }
  6. }

这段代码先返回了文档节点$root的子节点,然后利用foreach来遍历DOMNodeList对象。输出结果如下
#text
bookinfo
#text
preface
#text
chapter
#text

book元素包含了三个子原属,而且也包含了一些换行符或制表符之类的空白,这些空白在加载XML的时候没有去掉,因此输出结果中有#text。如何在没有去掉XML空白的情况下在结果中去掉#text呢,nodeType属性派上用场了。例如:

  1. foreach($children as $node) {
  2.     if ($node->nodeType != XML_TEXT_NODE) {
  3.         print $node->nodeName."\n";
  4.     }
  5. }

这样就可以去掉那些文本节点(text node),输出结果也没有#text字符了。

———————————————–
接下来不翻译了,改成笔记,翻译的废话太多了 >. 读者可自己运行代码后思考一下,这样更容易上手 ^^
-----------------------------------------------

访问特定节点

如果要访问特点节点可以使用节点的标签名。可使用的方法有getElementsByTagName()和getElementsByTagNameNS(),被访问的节点只能包含在文档节点(document node)和元素节点(element node)中,也就是说,要访问的节点必须是基于DOMDocument或DOMElement类。比如文档节点$dom,可以用getElementsByTagName()方法来取得文档中的所有title节点:

  1. $elements = $dom->getElementsByTagName("title");
  2. $length = $elements->length;
  3. for ($x=0;$x < $length;$x++) {
  4.     print "Element Value: ".$elements->item($x)->nodeValue."\n";
  5. }

getElementsByTagName()返回了一个DOMNodeList对象$elements,包含了$dom领域内的所有title元素。length属性返回了DOMNodeList对象中元素的个数。

可以使用通配符*作为参数来取得所有的元素:

  1. $preface = $root->getElementsByTagName("preface");
  2. $elements = $preface->item(0)->getElementsByTagName("*");
  3. $length = $elements->length;
  4. for ($x=0;$x < $length;$x++) {
  5.     print "Element Name: ".$elements->item($x)->nodeName."\n";
  6.     print "Element Value: ".$elements->item($x)->nodeValue."\n";
  7. }

这段代码通过文档元素$root以一个DOMNodeList对象返回了$root之内的所有preface元素并赋值给$preface。接着,返回取得的DOMNodeList对象中的第一个值,然后直接执行getElementsByTagName(”*”)。所有$preface内的元素都以DOMNodeList对象返回到$elements中。最后利用for循环输出所有的元素名称及元素值。输出结果如下:
Element Name: title
Element Value: The DOM Tree
Element Name: para
Element Value: An example DOM Tree using DocBook.

在处理有命名空间的文档时,可以使用getElementsByTagNameNS()方法来返回指定的命名空间的元素。这个方法有两个参数,第一个是命名空间URI,第二个是元素标签的名称。命名空间URI参数也支持通配符*。可以用通配符来取得任一个命名空间的所有元素:

  1. $result = $dom->getElementsByTagNameNS("*", "*");

这行代码返回了一个DOMNodeList对象$result,返回了文档中有命名空间的所有元素。

访问属性(Accessing Attributes)

跟其它节点类型一样,节点的属性(attribute)继承了DOMNode类中的方法和属性(property),但是不能像其它节点一样访问它。
可以用hasAttributes()方法来检查一个节点是否有属性,用attributes属性(property)来取得节点的所有属性, hasAttributes()和attributes都是在DOMNode类中定义的,因此可以在所有的节点中使用,但是只有在DOMElement对象中是由才会返回有意义的值。

  1. if ($root->hasAttributes()) {
  2.     $attributes = $root->attributes;
  3.     foreach($attributes as $attr) {
  4.         print "Attribute Name: ".$attr->nodeName."\n";
  5.         print "Attribute Value: ".$attr->nodeValue."\n";
  6.     }
  7. }

在这段代码中,如果$root所指代的节点存在属性的话hasAttributes()返回true,接着利用attributes属性返回一个DOMNamedNodeMap对象给$attributes。DOMNamedNodeMap跟DOMNodeList一样,对是可以循环的。输出结果如下:
Attribute Name: lang
Attribute Value: en

与DOMNodeList不同的是DOMNamedNodeMap可以使用名称来访问,而不只是使用位置。比如:

  1. $attr = $attributes->getNamedItem("lang");
  2. print "Attribute Name: ".$attr->nodeName."\n";
  3. print "Attribute Value: ".$attr->nodeValue."\n";
  1. if ($attributes->length > 0) {
  2.         $attr = $attributes->item(0);
  3.         print "Attribute Name: ".$attr->nodeName."\n";
  4.         print "Attribute Value: ".$attr->nodeValue."\n";
  5. }

这两段代码输出结果是一样的。

访问特定属性

用DOMNameNodeMap对象来访问属性只是访问属性的方法之一。DOMElement类提供了一些用来访问指定属性的方法,getAttribute(), getAttributeNode(), getAttributeNS(), and getAttributeNodeNS()

  1. /* Access lang attribute value directly */
  2. print "Attribute Value: ".$root->getAttribute("lang")."\n";
  3. /* Return the lang attribute node and access the returned attribute node */
  4. $attr = $root->getAttributeNode("lang");
  5. print "Attribute Value: ".$attr->nodeValue."\n";

这两段代码的输出结果是一样的。第一段代码返回了lang属性后直接输出;第二段代码先返回lang属性节点,接着输出节点值。

尽管Listing 6-1 中的文档没有使用命名空间,但是还是可以使用命名空间的方法来访问。

  1. print "Attribute Value: ".$root->getAttributeNS(NULL, "lang")."\n";
  2. $attr = $root->getAttributeNodeNS(NULL, "lang");
  3. print "Attribute Value: ".$attr->nodeValue."\n";

第一个参数是要访问的命名空间URI,因为Listing 6-1没有命名空间,因此指定为NULL。

创建和编辑树

文档节点(Document Nodes)

本章早些时候介绍了几种创建DOMDocument对象的方法。创建的对象包含一个文档类型声明,你可以使用DOMImplementation累来创建一个DOMDocument对象,首先创建一个DOMDocType对象来作为DOMImplementation的参数。 DOMImplementation对象中的方法可以使用静态方式访问。比如:

  1. $doctype = DOMImplementation:: createDocumentType("book",
  2.                   "-//OASIS//DTD DocBook XML V4.1.2//EN",
  3.                   "");
  4. $dom = DOMImplementation:: createDocument(NULL, "book", $doctype);

指定编码

  1. $dom->encoding = "UTF-8";

指定编码只影响输出的文档,不影响创建文档过程中的文档编码。

元素节点

创建元素

有两种方法可以用来创建元素,一是使用DOMDocument对象中createElement()和createElementNS()方法,二是利用DOMElement直接实例化一个DOMElement对象。

创建一个bookinfo元素

  1. $bookinfo = $dom->createElement("bookinfo");

这段代码返回了一个名为”bookinfo”的DOMElement对象类型元素给$bookinfo变量。

也可以创建一个包含内容的元素

  1. $bititle = $dom->createElement("title", "DOM in PHP 5");

虽然已经创建了两个元素,但是它们跟文档没有直接关系,是独立的两个元素。

创建一个带有命名空间的元素

  1. $biauthor = $dom->createElementNS(NULL, "author");
  2. $trash = $dom->createElementNS("", "tr:trash");

直接实例化DOMElement对象来创建元素

  1. $firstname = new DOMElement("firstname", "Rob");
  2. $surname = new DOMElement("surname", "Richards");
  3. $nsElement = new DOMElement("nse:myelement", NULL, "");

第三行代码的NULL说明不指定值给所创建的元素。

插入元素节点

插入元素节点的方法是从DOMNode类继承的,它们不仅可以用于元素节点,还可以用于其它类型的节点。

  1. $dom->documentElement->appendChild($bookinfo);

appendChild()方法有一个被用来指定为子节点的对象作参数,并返回所创建的节点。新插入的节点作为当前节点的最后一个子节点插入。

在插入author元素前可以先在author元素中插入firstname和surname节点。当将author节点插入文档树中时,firstname和surname也被插入文档中:

  1. $biauthor->appendChild($surname);
  2. $biauthor->insertBefore($firstname, $surname);

上面的代码也可以这么写

  1. $biauthor->appendChild($firstname);
  2. $biauthor->appendChild($surname);

然后将author插入bookinfo节点:

  1. $bookinfo->appendChild($biauthor);

属性节点

可以使用DOMElement中的方法操作属性节点,使用DOMDocument中的方法来直接实例化一个属性节点或使用DOMElement中的方法来创建,也可以使用DOMNode和DOMElement中的方法来插入和移动属性节点。

使用DOMDocument中的createAttribute方法创建属性节点

  1. /* Equivalent methods for creation of lang attribute */
  2. $lang = $dom->createAttribute("lang");
  3. $lang = $dom->createAttributeNS(NULL, "lang");

使用这种方法创建必须另外再给属性赋值,可以使用DOMElement中的nodeValue属性或DOMAttr中的value属性来指定一个值,如:

  1. /* Equivalent calls to set the value for the lang attribute to "en" */
  2. $lang->nodeValue = "en";
  3. $lang->value = "en";

使用DOMAttr创建一个属性节点对象,并直接赋值:

  1. $lang = new DOMAttr("lang", "en");

创建好的属性节点可以想插入元素节点一样插入到一个节点中,但插入的结果不是插入一个子节点,而是一个属性:

  1. /* Equivalent methods for inserting an attribute */
  2. $bookinfo->appendChild($lang);
  3. $bookinfo->insertBefore($lang, NULL);

也可以使用DOMElement中的setAttributeNode()和setAttributeNodeNS()方法在一个节点中插入一个属性。这两个方法必须有一个DOMAtrr对象类型的属性节点作为参数。

  1. /* Equivalent calls for this document as no namespaces are being used */
  2. $oldlang = $bookinfo->setAttributeNode($lang);
  3. $oldlang = $bookinfo->setAttributeNodeNS($lang);

DOMElement类有两个可以直接给一个节点创建属性的方法,setAttribute()和setAttributeNS()。利用这两个方法可以在不创建DOMAttribute对象的情况下就创建一个属性:

  1. /* Equivalent calls to create the lang attribute with value "en" */
  2. $bookinfo->setAttribute("lang", "en");
  3. $bookinfo->setAttributeNS(NULL, "lang", "en");

文本节点

文本节点非常简单,因为它们没有子节点和属性,也就是说它们只包含文本。

创建和插入文本节点

可以利用DOMDocment对象的createTextNode()方法来创建一个文本节点:

  1. /* Equivalent creation of DOMText objects */
  2. $yeartxt = $dom->createTextNode("2005");
  3. $yeartxt = new DOMText("2005");

可以在一行代码中创建节点并将节点插入元素,最后返回创建的节点:

  1. /* Create and Append a copyright element */
  2. $copyright = $bookinfo->appendChild(new DOMElement("copyright"));

下面的代码实现了跟上一段代码一样的功能:

  1. /* Create year element */
  2. $year = $dom->createElement("year");
  3. /* Append text node to set content */
  4. $year->appendChild($yeartxt);
  5. $copyright->appendChild($year);

另外一种创建文本节点的方法:

  1. /* Append a newly created holder element with content "Rob Richards" */
  2. $copyright->appendChild(new DOMElement("holder", "Rob Richards"));

处理文本

DOMText类是从DOMCharacterData类派生出来的,因此,这两个类中的方法都可以用来处理文本。

  1. /* If content is not whitespace then ... */
  2. if (! $yeartxt->isElementContentWhitespace()) {
  3.     /* Print substring at offset 1 and length 2: 00 */
  4.     print $yeartxt->substringData(1,2)."\n";
  5.     /* Append the string -2006 to the content and print output: 2005-2006 */
  6.     $yeartxt->appendData("-2006");
  7.     print $yeartxt->nodeValue."\n";
  8.     /* Delete content at offset 4 with length of 5 and print output: 2005 */
  9.     $yeartxt->deleteData(4,5);
  10.     print $yeartxt->nodeValue."\n";
  11.     /* Insert string "ABC" at offset 1 and print output: 2ABC005 */
  12.     $yeartxt->insertData(1, "ABC");
  13.     print $yeartxt->nodeValue."\n";
  14.     /* Replace content at ofset 1 with length of 3 with an empty string: 2005 */
  15.     $yeartxt->replaceData(1, 3, "");
  16.     print $yeartxt->nodeValue."\n";
  17. }

现在,上面所有对节点的操作创建了一个如下所示的XML文档:

  1. xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
  3. "">
  4. <book>
  5.     <bookinfo lang="en">
  6.         <title>DOM in PHP 5title>
  7.         <author>
  8.             <firstname>Robfirstname>
  9.             <surname>Richardssurname>
  10.         author>
  11.         <copyright>
  12.         <year>2005year>
  13.         <holder>Rob Richardsholder>
  14.         copyright>
  15.     bookinfo>
  16. book>

其它类型的节点

  1. /* Create a DOMDocumentFragment */
  2. $frag = $dom->createDocumentFragment();
  3. $frag = new DOMDocumentFragment();
  4. /* Create DOMComment */
  5. $comment = $dom->createComment("this is a comment");
  6. $comment = new DOMComment("this is a comment");
  7. /* Results in */
  8. /* Create DOMCDATASection */
  9. $cdata = $dom->createCDATASection("
  10. $cdata = new DOMCDATASection("
  11. /* Results in */
  12. /* Create DOMProcessingInstruction */
  13. $pi = $dom->createProcessingInstruction("php", "echo 'Hello World';");
  14. $pi = new DOMProcessingInstruction("php", "echo 'Hello World';");
  15. /* Results in  echo 'Hello World';?> */
  16. /* Create DOMEntityReference */
  17. $entityref = $dom->createEntityReference("lt");
  18. $entityref = new DOMEntityReference("lt");
  19. /* Results in < */
  1. $frag = $dom->createDocumentFragment();
  2. $frag->appendChild(new DOMElement("node1", "node1 value"));
  3. $frag->appendChild(new DOMElement("node2", "node2 value"));
  1. $frag = $dom->createDocumentFragment();
  2. $frag->appendXML("node1 valuenode2 value");

移除和替代节点

之前已经有一个方法setAttributeNode()可以用来移除和替代节点,如果有一个旧的节点跟新的节点同名的话, setAttributeNode()将移除旧的节点并以新的节点替代它,这只在属性类型的节点上奏效。对于其它节点可以使用replaceChild ()和removeChild()方法来实现。

首先创建一个文档:

  1. $doc = DOMDocument::loadXML('xml version="1.0"?>
  2. child1 content
  3. child2 content
  4. child3 content
  5. ');

接着要把child2元素删除,并把child3元素用newchild元素替代。第一步是取得这些元素。

  1. $root = $doc->documentElement;
  2. $child2 = $root->getElementsByTagName("child2")->item(0);
  3. $child3 = $root->getElementsByTagName("child3")->item(0);

接着删除$child2对象:

  1. $root->removeChild($child2);

这时候XML文档将会变成如下的结构:

  1. xml version="1.0"?>
  2. <root>
  3. <child1>child1 contentchild1>
  4. <child3>child3 contentchild3>
  5. root>

在文档中会留出原来元素所占用的行。接着创建一个newchild元素来替代child3。

  1. $oldchild = $root->replaceChild(new DOMElement("newchild", "new content"), $child3);

这样,XML文档变成如下结构:

  1. xml version="1.0"?>
  2. <root>
  3. <child1>child1 contentchild1>
  4. <newchild>new contentnewchild>
  5. root>

那一行空白也是一个节点,可以用下面的代码删除:

  1. $children = $root->childNodes;
  2. for ($x=$children->length; $x--; $x>=0) {
  3.     $node = $children->item($x);
  4.     if ($node->nodeType == XML_TEXT_NODE && $node-> isElementContentWhitespace()) {
  5.         $root->removeChild($node);
  6.     }
  7. }

使用XPath

从PHP5.0开始,可以使用DOMXPath对象的query()方法返回一个包含节点的DOMNodeList对象。PHP5.1开始提供了一个evaluate()方法,它可以返回更多类型的节点对象。

实例化DOMXPath

使用new关键字创建一个DOMXPath对象,并传入一个DOMDocument对象类型的参数。

  1. $domxpath = new DOMXPath($dom);

使用query()方法

PHP5的所有版本都有query()方法,可以利用这个方法和XPath表达式从树形结构获取一些节点,以DOMNodeList对象的形式返回,如果没有获取到任何一个节点就返回一个空的DOMNodeList。可以将一个XPath表达式作为一个参数传给query()方法,比如:

  1. $list = $domxpath->query("/book/bookinfo/author");
  2. $author = $list->item(0);
  3. $list = $domxpath->query("surname", $author);
  4. $surname = $list->item(0);

如果你试图利用XPath表达式返回一个字符串,你会发现返回的是一个空的DOMNodeList对象:

  1. $list = $domxpath->query("string('/book/bookinfo/author/surname')");
  2. var_dump($list);
  3. print "Number of Nodes Returned: ".$list->length."\n";

length返回0。

使用evaluate()方法

PHP5.1开始增加了evaluate()方法,可以像query()方法一样使用它。

  1. $list = $domxpath->evaluate("string(/book/bookinfo/author/surname)");
  2. var_dump($list);

var_dump()函数输出string(8) “Richards”。说明evaluate()方法实现了query()所不能实现的功能。

evaluate()方法返回值的类型很多,有Boolean,Integer,String,Null和DOMNodeList,返回什么类型的值取决于使用你所使用的XPath表达式,如:

  1. $newyear = $domxpath->evaluate("number(/book/bookinfo/copyright/year) + 1");
  2. var_dump($newyear);

像query()方法一样,evaluate()可以返回一个包含多个节点的DOMNodeList对象,如:

  1. $list = $domxpath->evaluate("/book/bookinfo/author");
  2. $author = $list->item(0);
  3. print $author->nodeName."\n";
阅读(3434) | 评论(2) | 转发(0) |
0

上一篇:SimpleXML

下一篇:HTTP状态码

给主人留下些什么吧!~~

chinaunix网友2009-04-01 13:41:53

不错,不错

chinaunix网友2009-03-12 20:08:03

嗯。写得好。。。