PHP5的XML新特性

news/2024/12/23 7:28:34

作者 Christian Stocker 翻译 ice_berg16(寻梦的稻草人)

面向的读者

这篇文章的面向对象是所有对PHP5XML新功能感兴趣的各个水平的PHP开发者。我们假定读者掌握XML的基本知识。然而,如果你已经在你的PHP当中使用了XML,那么这篇文章也会让你受益非浅。

介绍

在当今的互联网世界,XML已经不再是一个时髦词了,它已经被广泛的接受和规范的使用了。因此相对于PHP4PHP5对于XML的支持更受到了重视。在PHP4中你面对的几乎都是非标准,API中断,内存泄漏以及其它不完全的功能。尽管有些不足已经在PHP4.3中得到改进,开发者们还是决定抛弃原有的代码,在PHP5重写全部代码。

这篇文章将对PHP5中关于XML的所有令人激动的新特性逐一介绍。

PHP4 XML

早期的PHP版本就已经开始支持XML了,而这只是一个基于SAX的接口,它可以轻松的解析任何XML文档。随着PHP4中加入了DOMXML扩展模块,XML被更好的支持了。后来XSLT做为补充被加了进来。在整个PHP4的阶段,其它一些功能如HTMLXSLTDTD验证也被加到了DOMXML扩展中,不幸的是,由于XSLTDOMXML扩展始终处于实验阶段,API部分也被不止一次的修改,它们还是不能以默认方式安装。此外,DOMXML扩展没有遵循W3C制定的DOM标准,而有自己的命名方法。虽然在PHP4.3中这部分得到了改善并且许多内存泄漏和其它一些功能也得以修复,但它始终没有发展到一个稳定的阶段,一些深入的问题已经几乎不可能修复只有SAX扩展被已默认方式安装,其它的一些扩展从未得到广泛的使用。

基于所有这些原因,PHPXML开发者决定在PHP5重写全部代码,并遵循使用标准。

PHP5XML
PHP5中所有支持XML的部分几乎全部重新编写.现在的所有XML扩展都是基于GNOME项目的LIBXML2库。这将允许在不同的扩展模块之间互相操作,核心开发者只需要在一个底层的库上进行开发。例如,复杂的内存管理只实现一次就可以让所有XML相关扩展得到改善。

除了继承PHP4中闻名的SAX解析器之外,PHP5还支持遵循W3C标准的DOM和基于LIBXSLT引擎的XSLT。同时还加入了PHP独有的SimpleXML扩展和符合标准的SOAP扩展。随着XML越来越被重视,PHP开发者决定在默认安装方式中加入更多对XML的支持。这就意味着你现在可以使用SAXDOMSimpleXML,而这些扩展将会在更多的服务器上安装。然后对于XSLTSOAP的支持,还需要在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的文档加入书签。它与PHP5DOM部分基本上相同。

在这篇文章的大多数例子中我们将使用同一个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,对象,而不是像PHP4get_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 就像是XMLSQL,使用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对象,使用所有能用的方法和特性,比如XPathSimpleXML

当你需要访问一个你无法控制站点的内容时,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中写自己的验证程序,因为你可以使用三个应用最广泛的标准之一(DTDXML 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');

目前,所有这些都只会简单的返回truefalse,错误会被做为PHP警告输出。显然想返回给用户友好的信息这并不是一个好主意,在PHP5.0以后的版本里会有所改善。到底该怎么实现目前还在讨论之中,但是错误报告肯定会处理的更好。

SimpleXML

SimpleXML PHPXML家族中最后一个被加入的成员,加入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的,因此也更符合PHP5XML概念。

理论上将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的版本中会实现这个功能。

摘要

PHPXML的支持已经向前迈进了一大步,它符合标准,功能强大,互用协作性强,被作为默认选项安装已被授权使用。新加入的SimpleXML扩展提供了简单快速访问XML文档的方法,可以节省你很多的代码,尤其是当你有结构化文档或者可以使用强大的XPath时。

感谢libxml2—PHP5 XML扩展所使用的底层库,使用DTDRelaxNGXML Schema验证XML文档现在已经被支持了。

XSL支持也得到了翻新,现在使用Libxslt库,比原来的Sablotron库在性能上有很大提高,而且,在XSLT样式表内部调用PHP函数可以让你写出更强大的XSLT代码。

如果你已经在PHP4或其它语言中使用了XML,你会喜欢PHP5XML特性的,XMLPHP5中有了很大的变化,符合标准,和其它工具,语言是同等的。

链接

PHP 4 相关

  • Domxml 扩展: http://www.php.net/domxml/
  • Sablotron 扩展: http://www.php.net/xslt/
  • Libxslt: http://www.php.net/manual/en/function.domxml-xslt-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

http://www.niftyadmin.cn/n/4072842.html

相关文章

c++ java 选择排序,C++模板元编程实现选择排序

前言模板在C一直是比较神秘的存在。 STL 和 Boost 中都有大量运用模板,但是对于普通的程序员来说,模板仅限于使用。在一般的编程中,很少会有需要自己定义模板的情况。但是作为一个有理想的程序员,模板是一个绕不过去的坎。由于C标…

产品经理十六章:如何提升项目管理效率

在项目团队成立之后,项目经理就是船长,他不仅仅要根据航行过程中的情况控制航行的方向和路线,而且要负责管理其他所有与成功到达终点相关的工作。 为了提升项目管理效率,项目经理还应该重点做好以下工作: 一、严格控制…

ridgelet matlab,第一代curvelet变换matlab程序

【实例简介】用于图像处理的Curelet变换常用工具箱,有VC,Matlab等实现的代码。【实例截图】【核心代码】curvelet_first_generation├── curvelet_first_generation│ ├── addwgn.m│ ├── barbara_256x256.jpg│ ├── cameraman.tif│ ├── car…

互联网之父:文登•瑟夫

互联网每一步关键的进化背后,都有一位了不起的的奠基人。现年67岁的文登•瑟夫(Vinton G. Cerf)就是这样一位当之无愧的“互联网之父”。他和罗伯特•卡恩一起构建了TCP,使其成为标准走向世界。作为因特网方面为数不多的权威之一,Cerf作为TCP…

Linux用户管理(八)Shell编程基础

Shell编程一. Shell的基本概念n Linux shell是内置编程语言。提供管道、命令替换、自动补齐机制n BashShell(bash):Bourne Shell的增强版,Linux系统的默认Shell二. Bash Shell编程基础1.Shell的变量和参数&#xff…

matlab图像平均背景建模,【Opencv手记】平均背景法建模提取前景

#include "highgui.h"#include "cv.h"#include "cxcore.h"/*为不同临时图像和统计属性图像创建指针*///三通道float图像IplImage *IavgF,*IdiffF,*IprevF,*IhiF,*IlowF;IplImage *Iscratch,*Iscratch2;//单通道float图像IplImage *Igray1,*Igray…

LVN与其在Linux上的实现

参考资料: LVM详解-骏马金龙-博客园 How to reduce the size of an LVM partition formatted with xfs filesystem on CentOS7? 骏马兄的博文会相对深入一点,并且他是基于ext系列文件系统来演示扩容与缩容,而我使用的是xfs文件系统。 基本概…

dubbo的一次请求源码分析

调用某个服务首先会进入到动态代理。 InvokerInvocationHandler#invoke(Object proxy, Method method, Object[] args)public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName method.getName();Class<?>[] parameter…