PHP5正式发布
PHP5 的XML新特性
PHP4 的 XML
早期的PHP版本就已经开始支持XML了,而这只是一个基于SAX的接口,它可以轻松的解析任何XML文档。随着PHP4中加入了DOMXML扩展模块,XML被更好的支持了。后来XSLT做为补充被加了进来。在整个PHP4的阶段,其它一些功能如HTML,XSLT和DTD验证也被加到了DOMXML扩展中,不幸的是,由于XSLT和DOMXML扩展始终处于实验阶段,API部分也被不止一次的修改,它们还是不能以默认方式安装。此外,DOMXML扩展没有遵循W3C制定的DOM标准,而有自己的命名方法。虽然在PHP4.3中这部分得到了改善并且许多内存泄漏和其它一些功能也得以修复,但它始终没有发展到一个稳定的阶段,一些深入的问题已经几乎不可能修复。只有SAX扩展被已默认方式安装,其它的一些扩展从未得到广泛的使用。
基于所有这些原因,PHP的XML开发者决定在PHP5重写全部代码,并遵循使用标准。
PHP5的XML在PHP5中所有支持XML的部分几乎全部重新编写.现在的所有XML扩展都是基于GNOME项目的LIBXML2库。这将允许在不同的扩展模块之间互相操作,核心开发者只需要在一个底层的库上进行开发。例如,复杂的内存管理只实现一次就可以让所有XML相关扩展得到改善。
除了继承PHP4中闻名的SAX解析器之外,PHP5还支持遵循W3C标准的DOM和基于LIBXSLT引擎的XSLT。同时还加入了PHP独有的SimpleXML扩展和符合标准的SOAP扩展。随着XML越来越被重视,PHP开发者决定在默认安装方式中加入更多对XML的支持。这就意味着你现在可以使用SAX,DOM和SimpleXML,而这些扩展将会在更多的服务器上安装。然后对于XSLT和SOAP的支持,还需要在PHP编译时被显式的配置。
数据流的支持
现在所有的XML扩展都支持PHP数据流,即使你不从PHP中直接访问。例如,在PHP5中你可以从一个文件或从一条指令访问数据流。基本上你能够在任何可以访问普通文件的地方访问PHP数据流。
PHP4.3中简要的介绍了数据流,在PHP5中已经得到了进一步的提高,包含文件存取,网络存取和其它操作,如共享一套功能函数。你甚至可以使用PHP代码来实现你自己的数据流,这样数据存取将变得非常简单。关于这部分的更多细节请参考PHP文档。
SAX
SAX的全称是Simple API for XML,它是用于解析XML文档的接口,是基于回调形式的。从PHP3开始就已经支持了SAX,到现在也没有太大的变化。在PHP5中,API接口并没有改变,所以你的代码仍然可以运行。唯一不同的是它不再基于EXPAT库,而是基于LIBXML2库。
这个变化带来了一些对命名空间支持上的问题,这个问题在LIBXML2.2.6版本中已经得到解决。但是LIBXML2以前的版本中并没有解决,因此如果你使用了xml_parse_create_ns();强烈建议在你的系统上安装LIBXML2.2.6。
DOM
DOM (文档对象模型)是由W3C制定的一套访问XML文档树的标准。在PHP4可以使用DOMXML来对此进行操作,DOMXML的最主要问题是它不符合标准的命名方法。而且在很长一段时间内还存在内存泄漏问题(PHP4.3已经修复了这个问题)。
新的DOM扩展是基于W3C标准完成的,包含方法和属性名称。如果你在其它语言中熟悉DOM,例如在JavaScript中,那么在PHP中编写类似的功能将变得非常容易。你不必每次都查看文档,因为方法和参数都是相同的。
由于使用了新的W3C标准,基于DOMXML的代码将不能运行。在PHP中的API有很大的不同。但是如果你的代码中使用了类似W3C标准的方法命名方式,移植并不是很困难。你只需要将载入函数和保存函数修改,删除函数名中的下划线(DOM标准使用首字母大写)。其它各处的调节当然也是必须的,但是主要逻辑部分可以保持不变。
读取DOM
我不会在这篇文章中解释DOM扩展的所有特性,那也是没有必要的。或许你应该将HTTP://www.w3.org/DOM的文档加入书签。它与PHP5的DOM部分基本上相同。
在这篇文章的大多数例子中我们将使用同一个XML文件,zend.com上有非常简单的RSS版本。将下面的文本粘贴到一个文本文件中并保存为articles.xml。
http://www.zend.com/zend/week/week172.php
http://www.zend.com/zend/tut/tut-hatwar3.php
要将这个例子载入到一个DOM对象,首先要创建一个DOMDocument对象,然后载入XML文件。
$dom = new DomDocument();$dom->load("articles.xml");
正像上面所提及的,你可以使用PHP的数据流来载入一个XML文档,你应该这样写:
$dom->load("file:///articles.xml");
(或者其它类型的数据流)
如果你想将XML文档输出到浏览器或做为标准标出,使用:
print $dom->saveXML();
如果你想把它保存成文件,请使用:
print $dom->save("newfile.xml");
(注意这样做会将文件大小发送到stdout)
当然这个例子没有太多的功能,让我们来做些更有用的。我们来取得所有的title元素。有很多方法可以办到,最简单的就是使用getElementsByTagName($tagname):
$titles = $dom->getElementsByTagName("title");foreach($titles as $node) { print $node->textContent . "\n";}
textContent属性并不是W3C标准,它可以让我们很方便的快速读取一个元素的所有文本节点,使用W3C的标准读取是下面这样:
$node->firstChild->data;
(这时候你要确保firstChild结点是你需要的文本结点,否则你还得遍历所有子结点来查找)。
另外一个要注意的问题是getElementsByTagName()返回一个DomNodeList,对象,而不是像PHP4中get_elements_by_tagname()那样返回一个数组,但是正像你在这个例子中看到的那样,你可以使用foreach语句轻松的遍历它。你也可以直接使用$titles->item(0)来访问结点。该方法将返回第一个title元素。
另一个取得所有title元素的办法是从根结点遍历,你可以看到,这个方法更复杂,但是如果你需要的不只是title元素的时候,这个方法也就更灵活。
foreach ($dom->documentElement->childNodes as $articles) { //如果节点是一个元素(nodeType == 1)并且名字是item就继续循环 if ($articles->nodeType == 1 && $articles->nodeName == "item") { foreach ($articles->childNodes as $item) { //如果节点是一个元素,并且名字是title就打印它 if ($item->nodeType == 1 && $item->nodeName == "title") { print $item->textContent . "\n"; } } }}
XPath
XPaht 就像是XML的SQL,使用XPath你可以在一个XML文档中查询符合一些模式语法的特定结点。想使用XPath获得所有title结点,只需要这么做:
$xp = new domxpath($dom);$titles = $xp->query("/articles/item/title");foreach ($titles as $node) { print $node->textContent . "\n";}?>
这样和使用getElementsByTagName()方法差不多,但是Xpath要强大的多,例如,如果我们有一个title元素是article的子元素(而不是item的子元素),getElementsByTagName()就会将它返回。而使用/articles/item/title语法,我们只会得到在指定深度和位置的title元素。这只是一个简单的例子,再深入一点可能是这样:
/articles/item[position() = 1]/title返回第一个item元素的所有
/articles/item/title[@id = '23'] 返回所有含有id属性并且值为23的title
/articles//title 返回所有articles元素下面的title(译者注://代表任意深度)
你也可以查询含有特殊兄弟元素的点,含有特殊文本内容的元素,或者使用命名空间等等。如果你必须大量的查询XML文档,适当的学习使用XPath会节省你很多时间,它使用简单,执行速度快,比标准的DOM需要更少的代码。
向DOM中写入数据
文档对象模型并不是只能读取和查询,你也可以操作和写入。(DOM标准有点冗长,因为编写者想尽量支持能够想像到的每一个环境,但是它工作的非常好)。看看下面这个例子,它在我们的article.xml文件中添加了一个新元素。
$item = $dom->createElement("item");$title = $dom->createElement("title");$titletext = $dom->createTextNode("XML in PHP5");$title->appendChild($titletext);$item->appendChild($title);$dom->documentElement->appendChild($item);print $dom->saveXML();
首先,我们创建了所有需要的结点,一个item元素,一个title元素和一个包含item标题的文本结点,然后我们将所有的结点链接起来,把文本结点加到title元素上,把title元素加到item元素上,最后我们把item元素插入到articles根元素上。现在,我们的XML文档中有一个新的文章列表了。
扩展类(class)
好了,上面的例子都可以在PHP4下面用DOMXML扩展来做(只是API有一些不同),能够自己扩展DOM类是PHP5的一个新特性,这使得书写更多可读性强的代码变得可能。下面是用DOMDocument类重新写的整个例子:
class Articles extends DomDocument { function __construct() { //必须调用! parent::__construct(); } function addArticle($title) { $item = $this->createElement("item"); $titlespace = $this->createElement("title"); $titletext = $this->createTextNode($title); $titlespace->appendChild($titletext); $item->appendChild($titlespace); $this->documentElement->appendChild($item); }}$dom = new Articles();$dom->load("articles.xml");$dom->addArticle("XML in PHP5");print $dom->save("newfile.xml");
HTML
PHP5中一个经常不被注意到的特性是libxml2库对HTML的支持,你不仅可以使用DOM扩展载入结构良好(well-formed)的XML文档,还可以载入非结构良好的(not-well-formed)HTML文档,把它当做标准的DOMDocument对象,使用所有能用的方法和特性,比如XPath和SimpleXML。
当你需要访问一个你无法控制站点的内容时,HTML的性能就显示十分有用了。在 XPath, XSLT 或 SimpleXML的帮助下,你省掉了许多代码,像使用正则表达式比较字符串或者SAX解析器。当HTML文档结构不是很好的时候,这个办法尤其有用(这是个频繁的问题!)。
下面的代码获得并解析php.net的首页,将返第一个title元素的内容。
$dom = new DomDocument();$dom->loadHTMLFile("http://www.php.net/");$title = $dom->getElementsByTagName("title");print $title->item(0)->textContent;
请注意当指定元素没有找到时,你的输出可能会包含错误。如果你的网站还在使用PHP输出HTML4代码,有一个好消息要告诉你,DOM扩展不仅能载入HTML文档,而且还能将他们保存为HTML4格式的文件。在你添加完DOM文档后,使用$dom->saveHTML()来保存。要注意的是,为了使输出的HTML代码符合W3C标准,最好不用使用整齐的扩展?(tidy extension)。Libxml2 库支持的HTML并不会考虑到每个可能发生的事情,也不能很好的处理非通用格式的输入。
验证
XML文档的验证越来越重要了。例如,如果你从一些国外资源中获得了一个XML文档,在你处理之前你需要检验它是否符合某个确定的格式。幸运的是你不需要在PHP中写自己的验证程序,因为你可以使用三个应用最广泛的标准之一(DTD,XML Schema 或RelaxNG)来完成它。.
DTD是一个产生于SGML时代的标准,缺少一些XML的新特性(如命名空间),而且由于它不是用XML写的,它也很难被解析和转换。
XML Schemai是由W3C制定的一个标准,它应用广泛,几乎包含了所有验证XML文档所需要的内容。
RelaxNG 是复杂的XML Schema标准的对头,是由自由者组织创建的,由于它比XML Schema更容易实现,越来越多的程序开始支持RelaxNG了
如果你没有遗留下来的计划文档或者非常复杂的XML文档,那么使用RelaxNG吧。它书写和阅读都比较简单,越来越多的工具也支持它。甚至还有一个工具叫Trang,它可以从XML范本中自动创建一个RelaxNG文档。而且只有RelaxNG(和老化的DTDS)被libxml2完全支持,尽管libxml2也即将完全支持ML Schema。
验证XML文档的语法相当简单:
$dom->validate('articles.dtd');
$dom->relaxNGValidate('articles.rng');
$dom->schemaValidate('articles.xsd');
目前,所有这些都只会简单的返回true或false,错误会被做为PHP警告输出。显然想返回给用户友好的信息这并不是一个好主意,在PHP5.0以后的版本里会有所改善。到底该怎么实现目前还在讨论之中,但是错误报告肯定会处理的更好。
SimpleXML
SimpleXML 是PHP的XML家族中最后一个被加入的成员,加入SimpleXML扩展的目的是为了提供一个使用标准对象属性和迭代器访问XML文档的更简单的方法。该扩展没有太多的方法,虽然如此它还是相当强大的。从我们的文档的取得所有title节点比原来需要更少的代码。
$sxe = simplexml_load_file("articles.xml");foreach($sxe->item as $item) { print $item->title ."\n";}
这是在干什么?首先将articles.xml载入到一个SimpleXML对象。然后取得所有$sxe中的item元素,最后$item->title返回title元素的内容,就是这样。你也可以使用关联数组查询属性,使用: $item->title['id']。
看到了吧,这后面真是太神奇了,有许多不同的办法可以得到我们想要的结果,例如, $item->title[0]返回和例子中相同的结果,另一方面,foreach($sxe->item->title as $item)只返回第一个title,并不是所有在文档中的title元素。(就像我在XPath中预期的那样)。
SimpleXML 实际上是使用了Zend引擎2新特性的第一个扩展。因此也成了这些新特性的测试点,你要知道在开发阶段bugs和不可预料的错误可不是少数。
除了上面例子中所使用的遍历所有节点的方法,在SimpleXML中也有一个XPath接口,它为访问单个结点提供了更简单的办法。
foreach($sxe->xpath('/articles/item/title') as $item) { print $item . "\n";}
不可否认,这段代码也不比前面例子中的短,但是提供了更复杂或更深的嵌套XML文档,你会发现和SimpleXML一起使用XPath会节省你很多的输入。