热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PHP的XML分析函数

PHP的XML分析函数适合的阅读对象本文适合于打算用XML编写程序的有经验PHP程序员。本文假设你熟悉XML的语法和优点。如果读者希望学习更多的XML知识,在阅读本文以前应浏览以下网站:WhatisXML?
PHP的XML分析函数


适合的阅读对象



本文适合于打算用XML编写程序的有经验PHP程序员。本文假设你熟悉XML的语法和优点。





如果读者希望学习更多的XML知识
在阅读本文以前应浏览以下网站





What is XML? - 由Normal Walsh撰写XML介绍

XML FAQ
-XML - 常见问题

Project Cool
: XML resources - XML指南和参考

XML
.com - XML入门网站

XML
.org - XML的连接网站

Annotated XML specification
- W3C的XML标准的解释

介绍



首先我得承认我喜欢计算机标准。如果每个人都遵从这个行业的标准
互联网将会是一个更好的媒体。使用标准化的数据交换格式才能使开放的和独立于平台的计算模式切实可行。这就是我作为XML爱好者的原因。



幸运的是
我最喜爱的脚本语言不但支持XML而且对其支持正不断加强。PHP可以让我迅速将XML文档发布到互联网上收集XML文档的统计信息将XML文档转换成其它格式。例如我时常用PHP的XML处理能力来管理我用XML所写的文章和书。



本文中
我将讨论任何用PHP内建的Expat解析器来处理XML文档。通过范例我将演示Expat的处理方法。同时范例可以告诉你如何



建立你自己的处理函数

将XML文档转换成你自己的PHP数据结构



介绍Expat



XML的解析器
同样称为XML处理器可以使程序访问XML文档的结构和内容。Expat是PHP脚本语言的XML解析器。它同时也运用在其它项目中例如Mozilla、Apache和Perl。



什么是基于事件的解析器




XML解析器的两种基本类型



基于树型的解析器将XML文档转换成树型结构。这类解析器分析整篇文章同时提供一个API来访问所产生树的每个元素。其通用的标准为DOM文档对象模式

基于事件的解析器
将XML文档视为一系列的事件。当一个特殊事件发生时解析器将调用开发者提供的函数来处理。

基于事件的解析器有一个XML文档的数据集中视图
也就是说它集中在XML文档的数据部分而不是其结构。这些解析器从头到尾处理文档并将类似于元素的开始、元素的结尾、特征数据的开始等等事件通过回调callback函数报告给应用程序。以下是一个"Hello-World"的XML文档范例



<greeting>

Hello World

</greeting>



基于事件的解析器将报告为三个事件




开始元素greeting

CDATA项的开始
值为Hello World

结束元素
greeting

不像基于树型的解析器
基于事件的解析器不产生描述文档的结构。在CDATA项中基于事件的解析器不会让你得到父元素greeting的信息。

然而
它提供一个更底层的访问这就使得可以更好地利用资源和更快地访问。通过这种方式就没有必要将整个文档放入内存而事实上整个文档甚至可以大于实际内存值。





Expat就是这样的一种基于事件的解析器。当然如果使用Expat
必要时它一样可以在PHP中生成完全的原生树结构。





上面Hello
-World的范例包括完整的XML格式。但它是无效的因为既没有DTD文档类型定义与其联系也没有内嵌DTD。





对于Expat
这并没有区别Expat是一个不检查有效性的解析器因此忽略任何与文档联系的DTD。但应注意的是文档仍然需要完整的格式否则Expat和其他符合XML标准的解析器一样将会随着出错信息而停止。





作为不检查有效性的解析器
Exapt的快速性和轻巧性使其十分适合互联网程序。





编译Expat



Expat可以编译进PHP3
.0.6版本或以上中。从Apache1.3.9开始Expat已经作为Apache的一部分。在Unix系统中通过-with-xml选项配置PHP你可以将其编译入PHP。





如果你将PHP编译为Apache的模块
而Expat将默认作为Apache的一部分。在Windows中你则必须要加载XML动态连接库。



XML范例
XMLstats



了解Expat的函数的一个办法就是通过范例。我们所要讨论的范例是使用Expat来收集XML文档的统计数据。





对于文档中每个元素
以下信息都将被输出



该元素在文档中使用的次数

该元素中字符数据的数量

元素的父元素

元素的子元素

注意
为了演示我们利用PHP来产生一个结构来保存元素的父元素和子元素



准备



用于产生XML解析器实例的函数为xml_parser_create
()。该实例将用于以后的所有函数。这个思路非常类似于PHP中MySQL函数的连接标记。在解析文档前基于事件的解析器通常要求你注册回调函数用于特定的事件发生时调用。Expat没有例外事件它定义了如下七个可能事件





对象 XML解析函数 描述



元素 xml_set_element_handler
() 元素的开始和结束



字符数据 xml_set_character_data_handler
() 字符数据的开始



外部实体 xml_set_external_entity_ref_handler
() 外部实体出现



未解析外部实体 xml_set_unparsed_entity_decl_handler
() 未解析的外部实体出现



处理指令 xml_set_processing_instruction_handler
() 处理指令的出现



记法声明 xml_set_notation_decl_handler
() 记法声明的出现



默认 xml_set_default_handler
() 其它没有指定处理函数的事件



所有的回调函数必须将解析器的实例作为其第一个参数
此外还有其它参数





对于本文最后的范例脚本。你需要注意的是它既用到了元素处理函数又用到了字符数据处理函数。元素的回调处理函数通过xml_set_element_handler
()来注册。





这个函数需要三个参数




解析器的实例

处理开始元素的回调函数的名称

处理结束元素的回调函数的名称

当开始解析XML文档时
回调函数必须存在。它们必须定义为与PHP手册中所描述的原型一致。





例如
Expat将三个参数传递给开始元素的处理函数。在脚本范例中其定义如下





function start_element($parser, $name, $attrs)





第一个参数是解析器标示
第二个参数是开始元素的名称第三参数为包含元素所有属性和值的数组。





一旦你开始解析XML文档
Expat在遇到开始元素是都将调用你的start_element()函数并将参数传递过去。





XML的
Case Folding选项



用xml_parser_set_option
()函数将Case folding选项关闭。这个选项默认是打开的使得传递给处理函数的元素名自动转换为大写。但XML对大小写是敏感的所以大小写对统计XML文档是非常重要的。对于我们的范例case folding选项必须关闭。





解析文档



在完成所有的准备工作后
现在脚本终于可以解析XML文档



Xml_parse_from_file()一个自定义的函数打开参数中指定的文件并以4kb的大小进行解析

xml_parse
()和xml_parse_from_file()一样当发生错误时即XML文档的格式不完全时将会返回false。

你可以使用xml_get_error_code
()函数来得到最后一个错误的数字代码。将此数字代码传递给xml_error_string()函数即可得到错误的文本信息。

输出XML当前的行数
使得调试更容易。

在解析的过程中
调用回调函数。

描述文档结构



当解析文档时
对于Expat需要强调问题的是如何保持文档结构的基本描述





如前所述基于事件的解析器本身并不产生任何结构信息。





不过标签
(tag)结构是XML的重要特性。例如元素序列<book><title>表示的意思不同于<figure><title>。也就是说任何作者都会告诉你书名和图名是没有关系的虽然它们都用到"title"这个术语。因此为了更有效地使用基于事件的解析器处理XML你必须使用自己的栈(stacks)或列表(lists)来维护文档的结构信息。





为了产生文档结构的镜像
脚本至少需要知道目前元素的父元素。用Exapt的API是无法实现的它只报告目前元素的事件而没有任何前后关系的信息。因此你需要建立自己的栈结构。





脚本范例使用先进后出
(FILO)的栈结构。通过一个数组栈将保存全部的开始元素。对于开始元素处理函数目前的元素将被array_push()函数推到栈的顶部。相应的结束元素处理函数通过array_pop()将最顶的元素移走。





对于序列
<book><title></title></book>栈的填充如下



开始元素book"book"赋给栈的第一个元素($stack[0])

开始元素title
"title"赋给栈的顶部($stack[1])

结束元素title
从栈中将最顶部的元素移去($stack[1])

结束元素title
从栈中将最顶部的元素移去($stack[0])

PHP3
.0通过一个$depth变量手动控制元素的嵌套来实现范例。这就使脚本看起来比较复杂。PHP4.0通过array_pop()和array_push()两个函数来使脚本看起来更简洁。





收集数据



为了收集每个元素的信息
脚本需要记住每个元素的事件。通过使用一个全局的数组变量$elements来保存文档中所有不同的元素。数组的项目是元素类的实例有4个属性类的变量



$count -该元素在文档中被发现的次数

$chars
-元素中字符事件的字节数

$parents
-父元素

$childs
- 子元素

正如你所看到的
将类实例保存在数组中是轻而易举的。





注意
PHP的一个特性是你可以通过while(list() = each())loop遍历整个类结构如同你遍历整个相应的数组一样。所有的类变量当你用PHP3.0时还有方法名都以字符串的方式输出。





当发现一个元素时
我们需要增加其相应的记数器来跟踪它在文档中出现多少次。在相应的$elements项中的记数元素也要加一。





我们同样要让父元素知道目前的元素是它的子元素。因此
目前元素的名称将会加入到父元素的$childs数组的项目中。最后目前元素应该记住谁是它的父元素。所以父元素被加入到目前元素$parents数组的项目中。





显示统计信息



剩下的代码在$elements数组和其子数组中循环显示其统计结果。这就是最简单的嵌套循环
尽管输出正确的结果但代码既不简洁又没有任何特别的技巧它仅仅是一个你可能每天用他来完成工作的循环。





脚本范例被设计为通过PHP的CGI方式的命令行来调用。因此
统计结果输出的格式为文本格式。如果你要将脚本运用到互联网上那么你需要修改输出函数来产生HTML格式。



总结



Exapt是PHP的XML解析器。作为基于事件的解析器
它不产生文档的结构描述。但通过提供底层访问这就使得可以更好地利用资源和更快地访问。





作为一个不检查有效性的解析器
Expat忽略与XML文档连接的DTD但如果文档的格式不完整它将会随着出错信息而停止。





提供事件处理函数来处理文档

建立自己的事件结构例如栈和树来获得XML结构信息标记的优点。

每天都有新的XML程序出现
而PHP对XML的支持也不断加强例如增加了支持基于DOM的XML解析器LibXML





有了PHP和Expat
你就可以为即将出现的有效、开放和独立于平台的标准作准备。



范例




/*****************************************************************************

* 名称:XML解析范例:XML文档信息统计

* 描述

* 本范例通过PHP的Expat解析器收集和统计XML文档的信息(例如:每个元素出现的次数、父元素和子元素

* XML文件作为一个参数 ./xmlstats_PHP4.php3 test.xml

* $Requires: Expat 要求:Expat PHP4.0编译为CGI模式

*****************************************************************************/




// 第一个参数是XML文件

$file = $argv[1];



// 变量的初始化

$elements = $stack = array();

$total_elements = $total_chars = 0;



// 元素的基本类

class element

{

var $count = 0;

var $chars = 0;

var $parents = array();

var $childs = array();

}



// 解析XML文件的函数

function xml_parse_from_file($parser, $file)

{

if(!file_exists($file))

{

die("Can't find file \"$file\".");

}



if(!($fp = @fopen($file, "r")))

{

die("Can't open file \"$file\".");

}



while($data = fread($fp, 4096))

{

if(!xml_parse($parser, $data, feof($fp)))

{

return(false);

}

}



fclose($fp);



return(true);

}



// 输出结果函数(方框形式)

function print_box($title, $value)

{

printf("\n+%'-60s+\n", "");

printf("|%20s", "$title:");

printf("%14s", $value);

printf("%26s|\n", "");

printf("+%'-60s+\n", "");

}



// 输出结果函数(行形式)

function print_line($title, $value)

{

printf("%20s", "$title:");

printf("%15s\n", $value);

}



// 排序函数

function my_sort($a, $b)

{

return(is_object($a) && is_object($b) ? $b->count - $a->count: 0);

}



function start_element($parser, $name, $attrs)

{

global $elements, $stack;



// 元素是否已在全局$elements数组中?

if(!isset($elements[$name]))

{

// 否-增加一个元素的类实例

$element = new element;

$elements[$name] = $element;

}



// 该元素的记数器加一

$elements[$name]->count++;



// 是否有父元素?

if(isset($stack[count($stack)-1]))

{

// 是-将父元素赋给$last_element

$last_element = $stack[count($stack)-1];



// 如果目前元素的父元素数组为空,初始化为0

if(!isset($elements[$name]->parents[$last_element]))

{

$elements[$name]->parents[$last_element] = 0;

}



// 该元素的父元素记数器加一

$elements[$name]->parents[$last_element]++;



// 如果目前元素的父元素的子元素数组为空,初始化为0



if(!isset($elements[$last_element]->childs[$name]))

{

$elements[$last_element]->childs[$name] = 0;

}



// 该元素的父元素的子元素记数器加一

$elements[$last_element]->childs[$name]++;

}



// 将目前的元素加入到栈中

array_push($stack, $name);

}



function stop_element($parser, $name)

{

global $stack;



// 从栈中将最顶部的元素移去

array_pop($stack);

}



function char_data($parser, $data)

{

global $elements, $stack, $depth;



// 增加目前元素的字符数目

$elements[$stack[count($stack)-1]]->chars += strlen(trim($data));

}



// 产生解析器的实例

$parser = xml_parser_create();



// 设置处理函数

xml_set_element_handler($parser, "start_element", "stop_element");

xml_set_character_data_handler($parser, "char_data");

xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);



// 解析文件

$ret = xml_parse_from_file($parser, $file);

if(!$ret)

{

die(sprintf("XML error: %s at line %d",

xml_error_string(xml_get_error_code($parser)),

xml_get_current_line_number($parser)));

}



// 释放解析器

xml_parser_free($parser);



// 释放协助元素

unset($elements["current_element"]);

unset($elements["last_element"]);



// 根据元素的次数排序

uasort($elements, "my_sort");



// 在$elements中循环收集元素信息

while(list($name, $element) = each($elements))

{

print_box("Element name", $name);



print_line("Element count", $element->count);

print_line("Character count", $element->chars);



printf("\n%20s\n", "* Parent elements");



// 在该元素的父中循环,输出结果

while(list($key, $value) = each($element->parents))

{

print_line($key, $value);

}

if(count($element->parents) == 0)

{

printf("%35s\n", "[root element]");

}



// 在该元素的子中循环,输出结果

printf("\n%20s\n", "* Child elements");

while(list($key, $value) = each($element->childs))

{

print_line($key, $value);

}

if(count($element->childs) == 0)

{

printf("%35s\n", "[no childs]");

}



$total_elements += $element->count;

$total_chars += $element->chars;

}



// 最终结果

print_box("Total elements", $total_elements);

print_box("Total characters", $total_chars);

?>

原文为Tobias Ratschiller 所著发表在http://www.zend.com上
推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了关于apache、phpmyadmin、mysql、php、emacs、path等知识点,以及如何搭建php环境。文章提供了详细的安装步骤和所需软件列表,希望能帮助读者解决与LAMP相关的技术问题。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
author-avatar
东yidd_154
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有