PHP5的XML新特性

作者 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 的阶段,其它一些功能如 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 会节省你很多的输入。
向 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 相关
PHP 5 相关
标准
工具
关于作者
Christian Stocker 是苏黎士 Bitflux GmbH 公司的创始人和 CEO ,他是 XSL DOM imagick 扩展的维护人员,德语书籍 PHP de Luxe 的合作作者,同时致力于其它开源项目,如 Bitflux Editor Popoon. 。可以通用 chregu@php.net. 和他联系。


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=52176
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值