php5 and xml示例
程序员文章站
2022-12-26 15:15:42
php5的xml新特性 作者 christian stocker 翻译 ice_berg16(寻梦的稻草人) 面向的读者 这篇文章...
php5的xml新特性
作者 christian stocker 翻译 ice_berg16(寻梦的稻草人)
面向的读者
这篇文章的面向对象是所有对php5的xml新功能感兴趣的各个水平的php开发者。我们假定读者掌握xml的基本知识。然而,如果你已经在你的php当中使用了xml,那么这篇文章也会让你受益非浅。
介绍
在当今的互联网世界,xml已经不再是一个时髦词了,它已经被广泛的接受和规范的使用了。因此相对于php4,php5对于xml的支持更受到了重视。在php4中你面对的几乎都是非标准,api中断,内存泄漏以及其它不完全的功能。尽管有些不足已经在php4.3中得到改进,开发者们还是决定抛弃原有的代码,在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的文档加入书签...糠只旧舷嗤?/a>
在这篇文章的大多数例子中我们将使用同一个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会节省你很多的输入。
向 simplexml 文档写入数据
你不仅可以解析和读取simplexml,而且还可以改变simplexml文档。至少我们加入一些扩展:
$sxe->item->title = "xml in php5 "; //title元素的新内容。
$sxe->item->title['id'] = 34; // title元素的新属性。
$xmlstring = $sxe->asxml(); // 将simplexml对象做为序列化的xml字符串返回
print $xmlstring;
互用协作性
由于simplexml也是基于libxml2库的,你可以在几乎不影响速度的情况下轻松的将simplexml对象转化成domdocument对象。(文档不用进行内部复制),由于这个机制,你拥有了二个对象的最好部分,使用一个适合你手头工作的工具吧,它是这样使用的:
$sxe = simplexml_import_dom($dom);
$dom = dom_import_simplexml($sxe);
xslt
xslt是用来将xml文档转换为其它xml文档的语言,xslt本身是用xml编写的,属于功能性语言家族,在程序处理上和面对对象语言(像php)有所不同。php4中有二种xslt处理器:sablotron(在广泛使用的xslt扩展中)和libxslt(在domxml扩展中),这两种api不互相兼容,并且使用方法也不相同。php5只支持libxslt处理器,之所以选择它是因为它是基于libxml2的,因此也更符合php5的xml概念。
理论上将sablotron绑定到php5上也是可能的,但是不幸的是没人来做。因此,如果你正在使用sablotron,你不得不在php5中切换到libxslt处理器。libxslt 是带有javascript异常处理支持的sablotron,甚至可以使用php强大的数据流来重新实现sablotron独有的计划处理(scheme handlers)。此外,libxslt 是 最快的xslt处理器之一,所以你还免费得到了速度的提升。(执行速度是sablotron的二倍)。
和本文讨论的其它扩展一样,你可以在xsl扩展,dom扩展和vice versa之间交换xml文档,实际上,你必须得这么做,因为ext/xsl扩展并没有载入和保存xml文档的接口,只能使用dom扩展。一开始学习xslt转换,你不需要掌握太多的内容,这里也不存在w3c标准,因为这个api中从mozilla“借”过来的。
首先你需要一个xslt样式表,将下列文本粘贴到一个新文件并且保存灰articls.xsl
然后用php脚本调用它::
/* 将xml和xsl文档载入到domdocument对象*/
$xsl = new domdocument();
$xsl->load("articles.xsl");
$inputdom = new domdocument();
$inputdom->load("articles.xml");
/* 创建xslt处理器,并导入样式表*/
$proc = new xsltprocessor();
$xsl = $proc->importstylesheet($xsl);
$proc->setparameter(null, "titles", "titles");
/* 转换并输出xml文档 */
$newdom = $proc->transformtodoc($inputdom);
print $newdom->savexml();
?>
上面的例子首先使用dom的方法load()载入xslt样式表articles.xsl,然后创建了一个新的xsltprocessor对象,该对象导到了后面要使用了xslt样式表对象,参数可以这样设置setparameter(namespaceuri, name, value),最后xsltprocessor对象使用transformtodoc($inputdom)开始转换并返回一个新的domdocument对象。
. 这个api的优点在于你可以使用同一个样式表转换许多xml文档,只需要将它载入一次然后重复使用它,因为transormtodoc()函数可以应用于不同的xml文档。
除了transormtodoc(),还有二个用于转换的方法:transformtoxml($dom)返回一个字符串,transformtouri($dom, $uri)将转换之后的文档保存到文件或一个php数据流。注意如果你想使用xslt的一个语法如 或 indent="yes",你不能使用transformtodoc(),因为domdocument对象 不能保存该信息,只能当你将转换后的结果直接保存到字符串或文件中时才能这样做。
调用php函数
xslt扩展最后一个新加的特性是能够在xslt 样式表内部调用任何php函数,主张正统的xml支持者一定不会喜欢这个功能(这样的样式表有点复杂,很容易混淆逻辑和设计),在某些地方却是十分有用的。当涉及到函数时xslt就变得很有限,即使想实现用不同的语言输出一个日期也是非常麻烦的。但是使用这个功能,处理这些就和只使用php一样容易。下面是向xslt添加一个函数的代码:
function datelang () {
return strftime("%a");
}
$xsl = new domdocument();
$xsl->load("datetime.xsl");
$inputdom = new domdocument();
$inputdom->load("today.xml");
$proc = new xsltprocessor();
$proc->registerphpfunctions();
// 载入文档并使用$xsl来处理
$xsl = $proc->importstylesheet($xsl);
/* 转换并输出xml文档 */
$newdom = $proc->transformtodoc($inputdom);
print $newdom->savexml();
?>
下面是xslt样式表datetime.xsl,它会调用这个函数。
下面是要使用样式表转换的xml文档,today.xml(同理,articles.xml也会得到同样结果)。
上面的样式表,php脚本和所有的xml文件会用当前系统设置的语言输出星期的名字。你可以给php:function()添加更多的参数,添加的参数会被传递给php函数。这里有一个函数php:functionstring(),这个函数自动将所有输入的参数转换为字符串,所以你不需要在php里进行转换。
注意你需要在转换之前调用$xslt->registerphpfunctions(),否则php函数调用将因为安全原因不会被执行(你始终相信你的xslt样式表吗?)。目前访问系统还没有实现,也许在将来php5的版本中会实现这个功能。
摘要
php对xml的支持已经向前迈进了一大步,它符合标准,功能强大,互用协作性强,被作为默认选项安装,已被授权使用。新加入的simplexml扩展提供了简单快速访问xml文档的方法,可以节省你很多的代码,尤其是当你有结构化文档或者可以使用强大的xpath时。
感谢libxml2—php5 xml扩展所使用的底层库,使用dtd,relaxng或xml schema验证xml文档现在已经被支持了。
xsl支持也得到了翻新,现在使用libxslt库,比原来的sablotron库在性能上有很大提高,而且,在xslt样式表内部调用php函数可以让你写出更强大的xslt代码。
如果你已经在php4或其它语言中使用了xml,你会喜欢php5的xml特性的,xml在php5中有了很大的变化,符合标准,和其它工具,语言是同等的。
链接
php 4 相关
domxml 扩展: http://www.php.net/domxml/
sablotron 扩展: http://www.php.net/xslt/
libxslt: http://www.php.net/manual/en/functi...-stylesheet.php
php 5 相关
simplexml: http://www.php.net/simplexml/
streams: http://www.php.net/manual/en/ref.stream.php
标准
dom: http://www.w3.org/dom
xslt: http://www.w3.org/tr/xslt
xpath: http://www.w3.org/tr/xpath
xml schema: http://www.w3.org/xml/schema
relaxng: http://relaxng.org/
xinclude: http://www.w3.org/tr/xinclude/
工具
libxml2, the underlying library: http://xmlsoft.org/
trang, a schema/relaxng/etc converter: http://www.thaiopensource.com/relaxng/trang.html
关于作者
christian stocker是苏黎士bitflux gmbh公司的创始人和ceo,他是xsl,dom和imagick扩展的维护人员,德语书籍php de luxe的合作作者,同时致力于其它开源项目,如bitflux editor和popoon.。可以通用chregu@php.net.和他联系。
上一篇: php 获取select下拉列表框的值
下一篇: php 破解防盗链图片函数