xml_set_element_handler

(PHP 4, PHP 5, PHP 7, PHP 8)

xml_set_element_handler设置开始和结束元素处理程序

说明

xml_set_element_handler(XMLParser $parser, callable $start_handler, callable $end_handler): true

为 XML parser 设置元素处理程序函数。

start_handler 在打开新的 XML 元素时被调用。 end_handler 在关闭 XML 元素时被调用。

参数

parser

XML 解析器。

start_handler

如果传递了 null 或空字符串,则处理程序将重置为其默认状态。

如果 handlercallable,则该可调用函数将被设置为处理程序。

如果 handlerstring,它可以是使用 xml_set_object() 设置的对象的方法的名称。

处理程序的签名必须为

start_element_handler(XMLParser $parser, string $name, array $attributes): void
parser
调用处理程序的 XML 解析器。
name
包含此处理程序被调用的元素的名称。如果 大小写折叠 对此解析器有效,则元素名称将以大写字母表示。
attributes
一个包含元素属性的关联数组。如果元素没有属性,则该数组为空。此数组的键是属性名称,值是属性值。属性名称 大小写折叠 的标准与元素名称相同。属性值 进行大小写折叠。 attributes 被遍历的顺序与属性声明的顺序相同。

end_handler

如果传递了 null 或空字符串,则处理程序将重置为其默认状态。

如果 handlercallable,则该可调用函数将被设置为处理程序。

如果 handlerstring,它可以是使用 xml_set_object() 设置的对象的方法的名称。

处理程序的签名必须为

end_element_handler(XMLParser $parser, string $name): void
parser
调用处理程序的 XML 解析器。
name
包含此处理程序被调用的元素的名称。如果 大小写折叠 对此解析器有效,则元素名称将以大写字母表示。

返回值

始终返回 true

变更日志

版本 说明
8.0.0 parser 现在期望一个 XMLParser 实例;以前,期望一个有效的 xml resource
添加注释

用户贡献的注释 15 个注释

2
rubentrancoso at gmail dot com
17 年前
我的 25 分钱。此示例展示了如何在关联数组树中解析 XML。

<?php

$file
= "flow/flow.xml";
$depth = 0;
$tree = array();
$tree['name'] = "root";
$stack[count($stack)] = &$tree;

function
startElement($parser, $name, $attrs) {
global
$depth;
global
$stack;
global
$tree;

$element = array();
$element['name'] = $name;
foreach (
$attrs as $key => $value) {
//echo $key."=".$value;
$element[$key]=$value;
}

$last = &$stack[count($stack)-1];
$last[count($last)-1] = &$element;
$stack[count($stack)] = &$element;

$depth++;
}

function
endElement($parser, $name) {
global
$depth;
global
$stack;

array_pop($stack);
$depth--;
}

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!(
$fp = fopen($file, "r"))) {
die(
"could not open XML input");
}

while (
$data = fread($fp, 4096)) {
if (!
xml_parse($xml_parser, $data, feof($fp))) {
die(
sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
$tree = $stack[0][0];
echo
"<pre>";
print_r($tree);
echo
"</pre>";
3
aw at avatartechnology dot com
19 年前
作为对 landb at mail dot net 的回应...

正如注释中提到的,当您需要时,可以传递一个包含对对象的引用和方法名的数组......因此您可以像这样调用您自己类中的方法作为处理程序

xml_set_element_handler($parser, array($this,"_startElement"), array($this,"_endElement"));

希望有帮助...
2
youniforever at naver dot com
19 年前
<html>
<head>
<title>SAX 演示</title>
<META HTTP-EQUIV='Content-type' CONTENT='text/html; charset=euc-kr'>
</head>
<body>
<h1>RSS ??????</h1>

<?php

$file
= "data.xml";

$currentTag = "";
$currentAttribs = "";

function
startElement($parser, $name, $attribs)
{
global
$currentTag, $currentAttribs;
$currentTag = $name;

$currentAttribs = $attribs;
switch (
$name) {

default:
echo(
"<b>&lt$name&gt</b><br>");
break;
}
}

function
endElement($parser, $name)
{
global
$currentTag;
switch (
$name) {
default:
echo(
"<br><b>&lt/$name&gt</b><br><br>");
break;
}
$currentTag = "";
$currentAttribs = "";
}

function
characterData($parser, $data)
{
global
$currentTag;
switch (
$currentTag) {
case
"link":
echo(
"<a href=\"$data\">$data</a>\n");
break;
case
"title":
echo(
"title : $data");
break;
default:
echo(
$data);
break;
}
}

$xmlParser = xml_parser_create();

$caseFold = xml_parser_get_option($xmlParser,
XML_OPTION_CASE_FOLDING);

$targetEncoding = xml_parser_get_option($xmlParser,
XML_OPTION_TARGET_ENCODING);

if (
$caseFold == 1) {
xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false);
}

xml_set_element_handler($xmlParser, "startElement", "endElement");
xml_set_character_data_handler($xmlParser, "characterData");

if (!(
$fp = fopen($file, "r"))) {
die(
"Cannot open XML data file: $file");
}

while (
$data = fread($fp, 4096)) {
if (!
xml_parse($xmlParser, $data, feof($fp))) {
die(
sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xmlParser)),
xml_get_current_line_number($xmlParser)));
xml_parser_free($xmlParser);
}
}
xml_parser_free($xmlParser);
?>
</table>
</body>
</html>
1
lloeki at gmail dot com
17 年前
我修改了之前的脚本,使其成为关联数组。我觉得这样更实用。顺便说一句,我更喜欢使用 strtolower() 函数,但这并不是强制性的。

<?php

$file
= "data.xml";
$depth = 0;
$tree = array();
$tree['name'] = "root";
$stack[] = &$tree;

function
startElement($parser, $name, $attrs) {
global
$depth;
global
$stack;
global
$tree;

$element = array();
foreach (
$attrs as $key => $value) {
$element[strtolower($key)]=$value;
}

end($stack);
$stack[key($stack)][strtolower($name)] = &$element;
$stack[strtolower($name)] = &$element;

$depth++;
}

function
endElement($parser, $name) {
global
$depth;
global
$stack;

array_pop($stack);
$depth--;
}

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!(
$fp = fopen($file, "r"))) {
die(
"could not open XML input");
}

while (
$data = fread($fp, 4096)) {
if (!
xml_parse($xml_parser, $data, feof($fp))) {
die(
sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
$tree = end(end($stack));
echo
"<pre>";
print_r($tree);
echo
"</pre>";

?>
1
hendra_g at hotmail dot com
18 年前
我在处理 "ibjoel at hotmail dot com" 提出的关于自闭合标签的问题时,发现他/她编写的脚本并没有按预期工作。
我尝试了一些 PHP 函数和示例,最终编写了一些代码。虽然可能不是最优雅的解决方案,但它适用于 "ibjoel at hotmail dot com" 提供的数据。
数据需要从文件中读取,以便可以利用 fp。它仍然使用 xml_get_current_byte_index(resource parser) 的技巧,但这次,我检查了索引之前的最后两个字符,并测试它们是否为 "/>"。

<?php
/* myxmltest.xml:
<normal_tag>
<self_close_tag />
data
<normal_tag>data
<self_close_tag attr="value" />
</normal_tag>
data
<normal_tag></normal_tag>
</normal_tag>
*/

//## 全局变量 ##//
$file = "myxmltest.xml";
$character_data_on = false;
$tag_complete = true;

function
startElement($parser, $name, $attrs)
{
global
$character_data_on;
global
$tag_complete;

echo
"&lt;<font color=\"#0000cc\">$name</font>";
//## 打印属性 ##//
if (sizeof($attrs)) {
while (list(
$k, $v) = each($attrs)) {
echo
" <font color=\"#009900\">$k</font>=\"<font
color=\"#990000\">
$v</font>\"";
}
}
//## 标签仍然不完整,
//## 将在 endElement 或 characterData 中完成 ##//
$tag_complete = false;
$character_data_on = false;
}

function
endElement($parser, $name)
{
global
$fp;
global
$character_data_on;
global
$tag_complete;

//#### 测试自闭合标签 ####//
//## xml_get_current_byte_index(resource parser) 在此函数中运行时,将返回以下索引(用 * 表示):
//## 对于自闭合标签:<br />*
//## 对于单独的闭合标签:<div>character data*</div>
//## 所以要测试自闭合标签,我们可以只测试索引的最后两个字符
//###################################//

if (!$character_data_on) {
//## 记录当前 fp 位置 ##//
$temp_fp = ftell($fp);

//## 将 fp 指向结束元素字节索引前的两个字节 ##//
$end_element_byte_index = xml_get_current_byte_index($parser);
fseek($fp,$end_element_byte_index-2);

//## 获取结束元素字节索引前的最后两个字符 ##//
$validator = fgets($fp, 3);

//## 恢复 fp 位置 ##//
fseek($fp,$temp_fp);

//## 如果最后两个字符是 "/>" ##//
if ($validator=="/>") {
//// 完成自闭合标签 ////
echo " /&gt";
//// 否则它是一个单独的闭合标签 ////
} else echo "&gt&lt/<font color=\"#0000cc\">$name</font>&gt";
$tag_complete = true;
} else echo
"&lt/<font color=\"#0000cc\">$name</font>&gt";

$character_data_on = false;
}

function
characterData($parser, $data)
{
global
$character_data_on;
global
$tag_complete;

if ((!
$character_data_on)&&(!$tag_complete)) {
echo
"&gt";
$tag_complete = true;
}
echo
"<b>$data</b>";
$character_data_on = true;
}

$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
if (!(
$fp = fopen($file, "r"))) {
die(
"无法打开 XML 输入");
}

echo
"<pre>";
while (
$file_content = fread($fp, 4096)) {
if (!
xml_parse($xml_parser, $file_content, feof($fp))) {
die(
sprintf("XML 错误:%s 在第 %d 行",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
echo
"</pre>";
xml_parser_free($xml_parser);
?>
1
jg at jmkg dot net
23 年前
如果您使用的是一个用于 XML 解析的类,并且想要检查 xml_set_element_handler 的返回值(以防它失败),您必须在类的构造函数之外执行此操作。 在构造函数内部,PHP-4.0.5 会导致程序终止。

基本上,将所有 XML 初始化代码放在类的另一个函数中,并将其从构造函数中移出。
0
darien at etelos dot com
17 年前
这份文档有点不准确。我知道之前已经说过很多次了,但还是需要重申...

如果您使用的是 PHP4,您可能需要使用 xml_set_object(),而不是用一个包含两个元素的数组调用任何 xml_set_*_handler() 函数。它在 PHP5 上可以正常工作,但是将相同的代码移植到 PHP4 上,它会为每个您设置的处理器创建一个 $this 的副本(即使您使用 &$this)!

<?php
// 这段代码在 PHP4 上会神秘地失败。
$this->parser = xml_parser_create();
xml_set_element_handler(
$this->parser,
array(&
$this,"start_tag"),
array(&
$this,"end_tag")
);
xml_set_character_data_handler(
$this->parser,
array(&
$this,"tag_data")
);
?>

<?php
// 这段代码在 PHP4 上可以正常工作。
$this->parser = xml_parser_create();
xml_set_object($this->parser,&$this);
xml_set_element_handler(
$this->parser,
"start_tag",
"end_tag"
);
xml_set_character_data_handler(
$this->parser,
"tag_data"
);
?>
0
turan dot yuksel at tcmb dot gov dot tr
18 年前
“ibjoel at hotmail dot com” 所描述的方法需要 libxml2 作为 XML 解析器,它在 Expat 上不适用。有关简要说明,请参阅 xml_get_current_byte_index。
0
ibjoel at hotmail dot com
19 年前
我注意到在下面的例子中,以及我在这个网站上看到的关于在 HTML 中查看 XML 的所有例子中,自闭合标签(如 <br />)的外观没有保留。解析器无法区分 <tag /> 和 <tag></tag>,如果您的开始和结束元素函数类似于这些例子,那么这两个实例都将输出一个单独的开始和结束标签。我需要保留自闭合标签,而且花了一段时间才弄明白这个解决方法。希望这能帮到其他人...

开始标签保持打开状态,然后由它的第一个子节点(下一个开始标签或它的结束标签)完成。结束标签将使用 “ />” 或 </tag> 完成,具体取决于解析数据中开始标签和结束标签之间的字节数。
<?php
//$data=filepath or string
$data=<<<DATA
<normal_tag>
<self_close_tag />
data
<normal_tag>data
<self_close_tag attr="value" />
</normal_tag>
data
<normal_tag></normal_tag>
</normal_tag>
DATA;

function
startElement($parser, $name, $attrs)
{
xml_set_character_data_handler($parser, "characterData");
global
$first_child, $start_byte;
if(
$first_child) //close start tag if neccessary
echo "><br />";
$first_child=true;
$start_byte=xml_get_current_byte_index ($parser);
if(
count($attrs)>=1){
foreach(
$attrs as $x=>$y){
$attr_string .= " $x=\"$y\"";
}
}
echo
htmlentities("<{$name}{$attr_string}"); //unclosed starttag
}

function
endElement($parser, $name)
{
global
$first_child, $start_byte;
$byte=xml_get_current_byte_index ($parser);
if(
$byte-$start_byte>2){ //if end tag is more than 2 bytes from start tag
if($first_child) //close start tag if neccessary
echo "><br />";
echo
htmlentities("</{$name}>")."<br />"; //individual end tag
}else
echo
" /><br />"; // self closing tag
$first_child=false;

}

function
characterData($parser, $data)
{
global
$first_child;
if(
$first_child) //if $data is first child, close start tag
echo "><br />";
if(
$data=trim($data))
echo
"<font color='blue'>$data</font><br />";
$first_child=false;
}

function
ParseData($data)
{
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0);
if(
is_file($data))
{
if (!(
$fp = fopen($file, "r"))) {
die(
"could not open XML input");
}

while (
$data = fread($fp, 4096)) {
if (!
xml_parse($xml_parser, $data, feof($fp))) {
$error=xml_error_string(xml_get_error_code($xml_parser));
$line=xml_get_current_line_number($xml_parser);
die(
sprintf("XML error: %s at line %d",$error,$line));
}
}
}else{
if (!
xml_parse($xml_parser, $data, 1)) {
$error=xml_error_string(xml_get_error_code($xml_parser));
$line=xml_get_current_line_number($xml_parser);
die(
sprintf("XML error: %s at line %d",$error,$line));
}
}

xml_parser_free($xml_parser);
}

ParseData($data);
?>
0
tj at tobyjoe dot com
21 年前
看来标签处理程序之间不会互相阻塞(即使开始处理程序尚未完成,结束处理程序也会被调用)。如果您在计划应用程序时没有意识到这一点,可能会遇到麻烦。
-1
kok at nachon dot nl
15 年前
这是另一个检测空元素的例子。它适用于 libxml2。请注意,它处理缓冲区边界。

<?php

$depth
= 0; //当前深度,用于格式化输出
$empty = false; //标签是否为空
$offset = 0; //当前缓冲区在流中的起始索引

function tagStart($parser, $name, array $attribs) {
global
$depth, $empty, $data, $offset, $lastchar;
$idx = xml_get_current_byte_index($parser);
/* xml_get_current_byte_index 返回流中的索引,而不是缓冲区中的索引。*
*/

/* 检查索引是否在缓冲区内。 */
if (isset($data[$idx - $offset])) {
$c = $data[$idx - $offset];
} else {
/* 如果不在,则使用缓冲区的最后一个字符。 */
$c = $lastchar;
}
$empty = $c == '/';
echo
str_repeat("\t", $depth), "<$name", ($empty ? '/>' : '>'), "\n";
if (!
$empty) ++$depth;
}

function
tagEnd($parser, $name) {
global
$depth, $empty;
if (!
$empty) {
--
$depth;
echo
str_repeat("\t", $depth), "</$name>\n";
} else {
$empty = false;
}
}

$parser = xml_parser_create();
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($parser, 'tagStart', 'tagEnd');

$data1 = '
<test>
<empty att="3" />
<nocontent></nocontent>
<content>
<empty/>
<empty/>
</content>
<empty/'
;

$data2 = '>
<empty att="5" />
</test>
'
;

$data = &$data1;
$length = strlen($data1);
$lastchar = $data[$length-1];
xml_parse($parser, $data1);
$offset += $length;
$data = &$data2;
xml_parse($parser, $data2);
-2
vladimir-leontiev at uiowa dot edu
18 年前
似乎 characterData() 函数以 1024 个字符为单位获取字符;因此,如果标签之间的字符串长度超过 1024 个字符,则 characterData() 将被多次调用,即使它只对应于一对标签。我不知道这个特性(bug?)是否在任何地方有文档记录,我只是想提醒大家注意这一点;它曾经让我陷入困境。我在 Linux 上使用的是 PHP 4.3.10。
-1
redb
18 年前
下面的示例(BadParser)在进行一些更改后可以正常工作。

xml_set_element_handler ( $parser, array ( &$this, 'tagStart' ), array ( &$this, 'tagEnd' ) );
xml_set_character_data_handler ( $parser, array ( &$this, 'tagContent' ) );
-2
Anonymous
19 年前
作为对 avatartechnology dot com 上 aw 的回应...
作为对 landb at mail dot net 的回应...

当您的函数位于对象中时
小心!不要忘记在您的参数中添加:&(引用)。

xml_set_element_handler($parser, array(&$this,"_startElement"), array(&$this,"_endElement"));
--> xmlparse 将在您的对象上运行(良好)。

而不是
xml_set_element_handler($parser, array($this,"_startElement"), array($this,"_endElement"));
---> xmlparse 将在您的对象的副本上运行(通常不好)

Vin-s
(抱歉我的英语)
-2
Anonymous
22 年前
您可以使用类来解析 XML。只需看一下以下函数

xml_set_object
To Top