用PHP解析XSL
在php的应用当中,为做到数据和代码分离需要使用模板技术。pear、phplib及不少公司都提供了相关的模板。但他们有一个共同的缺点:就是没有统一的规范,给使用者带来很多不便。另外有关的教程和范例较少,也太初浅,不易做深层次的开发应用。 XSL是W3C组织的规范标准,随着XML的应用而发展起来。其教程随处可见,只要你有ie5就可使用。当然由于是新技术,在支持程度上尚显不足。 这里给大家介绍一种用用PHP解析XSL的方法。该方法仅使用PHP提供的XML函数,无须难以配置的XSLT。 先看一下例子。 将以下内容保存为resume.xml <?xml version="1.0" encoding="GB2312"?> <?xml:stylesheet type="text/xsl" href="resume2.xsl"?> <document> <resume> <alias>唠叨</alias> <name>徐祖宁</name> <sex>男</sex> <birthday>1948.10</birthday> <addr>安徽</addr> <email>czjsz_ah@stats.gov.cn</email> <icq> </icq> <oicq> </oicq> <skill>C/C++、VFP、PHP、JavaScript</skill> <homepage> </homepage> <date>2001-7-19</date> </resume> <resume> <alias>刁馋</alias> <name>保密</name> <sex>男</sex> <birthday> </birthday> <addr>黑龙江</addr> <email>yuepengfei@mail.banner.com.cn</email> <icq>166581208</icq> <oicq>7665656</oicq> <skill> </skill> <homepage> </homepage> <date>2001-8-15</date> </resume> <resume> <alias>sports98</alias> <name>保密</name> <sex>男</sex> <birthday> </birthday> <addr>四川</addr> <email>flyruns@hotmail.com</email> <icq>15787767</icq> <oicq>11599322</oicq> <skill> </skill> <homepage>http://www.hiviresearch.com/cgi/report/</homepage> <date>2002-1-5</date> </resume> </document>
将以下内容保存为resume1.xsl <?xml version="1.0" encoding="GB2312"?> <HTML xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <HEAD> <TITLE>个人简历</TITLE> </HEAD><BODY> <TABLE border="1" cellspacing="0" style="font-size:10pt"> <CAPTION style="font-size: 110%; font-weight: bold"> 版主信息 </CAPTION> <xsl:for-each select="document"> <TR> <TH>别名</TH> <TH>姓名</TH> <TH>性别</TH> <TH>所在地</TH> <TH>专长</TH> </TR> <xsl:for-each select="resume"> <TR> <TD><xsl:value-of select="alias"/></TD> <TD><xsl:value-of select="name"/></TD> <TD><xsl:value-of select="sex"/></TD> <TD><xsl:value-of select="addr"/></TD> <TD><xsl:value-of select="skill"/></TD> </TR> </xsl:for-each> </xsl:for-each> </TABLE> </BODY> </HTML> 将以下内容保存为resume2.xsl <?xml version="1.0" encoding="GB2312"?> <HTML xmlns:xsl="http://www.w3.org/TR/WD-xsl"> <HEAD> <TITLE>个人简历</TITLE> </HEAD><BODY> <xsl:for-each select="document"> <xsl:for-each select="resume"> <TABLE border="1" cellspacing="0" style="font-size:10pt"> <CAPTION style="font-size: 110%; font-weight: bold"> 版主信息 </CAPTION> <TR> <TH>别名</TH><TD><xsl:value-of select="alias"/></TD> <TH>姓名</TH><TD><xsl:value-of select="name"/></TD> <TH>性别</TH><TD><xsl:value-of select="sex"/></TD> <TH>所在地</TH><TD><xsl:value-of select="addr"/></TD> </TR> <TR> <TH>加入时间</TH> <TD colspan="7"><xsl:value-of select="date"/></TD> </TR> <TR> <TH>专长</TH> <TD colspan="7"><xsl:value-of select="skill"/></TD> </TR> <TR> <TH>ICQ</TH> <TD colspan="7"><xsl:value-of select="icq"/></TD> </TR> <TR> <TH>OICQ</TH> <TD colspan="7"><xsl:value-of select="oicq"/></TD> </TR> <TR> <TH>主页</TH> <TD colspan="7"><xsl:value-of select="homepage"/></TD> </TR> </TABLE> </xsl:for-each> </xsl:for-each> </BODY> </HTML> 在ie5以上浏览器上查看resume.xml,并可修改resume.xml中<?xml:stylesheet type="text/xsl" href="resume2.xsl"?> 的resume2.xsl为resume1.xsl,可看到页面的变化。当然由于不是所有的浏览器都支持这个转换,所以需要在服务器上进行转换。
将以下内容保存为xmltest.php <?php require_once "xsl_class.php"; $xml = new XML; $p = new XSL; $p->parser("resume2.xsl",$xml->parser("resume.xml")); $p->display(); ?> 变换其中的resume2.xsl,我们仍将看到不同的页面,只是以转变成HTML格式了。
相关的类: 类xml_class解析xml文档产生一个类似于domxml的结构 类xsl_class派生于xml_class,用于解析xsl文档并模拟xsl函数,其中template尚未实现。 ***************** xml_class.php ***************** <?php class Element { var $Element;// 这种节点用于文档中的任何元素。元素节点的子节点可以是其内容的元素节点、注释节点、处理信息节点以及文本节点。 var $Text;// 文档中出现的所有文本,都分组归入到文本节点中。文本节点不可以有同为文本节点的紧接着的前或后的兄弟节点。 var $Attribute; // 每一个元素节点都有一套自己附加的属性节点。默认的属性值以与指定属性一样的方法来处理。这些节点都没有子节点。 var $Namespace; // 对于每一个以xlmns:和属性节点开头的元素,都有一个名称空格节点。这些节点没有子节点。 var $ProcessingInstruction; // 每一个处理指令都有一个单独的节点。这些节点都没有子节点。 var $Comment; // 每一个都有一个注释节点。这些节点都没有子节点。 var $parents = array(); var $childs = array(); }
class xml { var $tm = array(); var $xml_parser; var $data = array(); var $element = ""; // 当前节点 var $stack = array(); // 缓存当前标头的相关参数 var $type;
function trustedFile($file) { // only trust local files owned by ourselves if (!eregi("^([a-z]+)://", $file) && fileowner($file) == getmyuid()) { return true; } return false; }
//处理元素的开始标头 function startElement($parser, $name, $attribs) { if($this->element != "") { array_push($this->stack,$this->element); } $this->element = array(Name => $name); if(sizeof($attribs)) { $this->element[Attribute] = $attribs; } }
//处理元素的结束标头 function endElement($parser, $name) { $element = array_pop($this->stack); if(is_array($element)) { $element[Element][] = $this->element; $this->element = $element; }else { $this->data[Root] = $this->element; $this->element = ""; } }
//处理字元资料标头 function characterData($parser, $data) { $data = eregi_replace("^ +","",$data); $data = eregi_replace("^\n+","",$data); if(strlen($data) > 0) { $this->element[Text] .= $data; } }
//处理指令标头 function PIHandler($parser, $target, $data) { switch(strtolower($target)) { case "php": global $parser_file; // If the parsed document is "trusted", we say it is safe // to execute PHP code inside it.If not, display the code // instead. if($this->trustedFile($parser_file[$parser])) { eval($data); } else { $this->tm[] = sprintf("Untrusted PHP code: <i>%s</i>", htmlspecialchars($data)); } break; default: //echo $target; //echo "==".$data; //echo printf("%s %s",$target,$data); break; } }
//处理内定标头 function defaultHandler($parser, $data) { if(substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") { $this->tm[] = sprintf('<font color="#aa00aa">%s</font>', htmlspecialchars($data)); }else { $this->tm[] = sprintf('<font size="-1">%s</font>', htmlspecialchars($data)); } }
//处理外部实体参引标头 function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId, $publicId) { if ($systemId) { $p = new xml; return $p->parser($systemId); } return false; }
function parser($file) { global $parser_file;
if(!($fp = @fopen($file, "r"))) { return false; } $this->xml_parser = xml_parser_create(); xml_set_object($this->xml_parser, &$this);//使 XML 剖析器用对象
xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, 1); xml_set_element_handler($this->xml_parser, "startElement", "endElement"); xml_set_character_data_handler($this->xml_parser, "characterData"); xml_set_processing_instruction_handler($this->xml_parser, "PIHandler"); xml_set_default_handler($this->xml_parser, "defaultHandler"); xml_set_external_entity_ref_handler($this->xml_parser, "externalEntityRefHandler");
$this->type = xml_parser_get_option($this->xml_parser, XML_OPTION_CASE_FOLDING); while($data = fread($fp, 4096)) { if(!xml_parse($this->xml_parser, $data, feof($fp))) { die(sprintf("XML error: %s at line %d\n", xml_error_string(xml_get_error_code($xml_parser)), xml_get_current_line_number($xml_parser))); return false; } } xml_parser_free($this->xml_parser); return $this->data; } } ?>
******************** xsl_class.php ********************
<?php require_once "xml_class.php";
class xsl extends xml { var $datastack = array(); var $sp; function parser($file,$dsn=null) { parent::parser($file); if($dsn != null) { $this->dsn[Element][0] = $dsn[Root]; } } //处理元素的开始标头 function startElement($parser, $name, $attribs) { if(eregi("^XSL:",$name)) { $ar = split(":",$name); return array_push($this->data,array(xsl => $ar[1],command => $attribs)); } if(sizeof($attribs)) { $att = ""; while(list($k, $v) = each($attribs)) { $att .= " $k=\"$v\""; } array_push($this->data,array(tag => "$name$att")); }else array_push($this->data,array(tag => "$name")); }
//处理元素的结束标头 function endElement($parser, $name) { if(!eregi("^XSL:",$name)) { array_push($this->data,array(tag => "/$name")); }else { $ar = split(":",$name); array_push($this->data,array(xsl => "/$ar[1]")); } }
//处理字元资料标头 function characterData($parser, $data) { $data = eregi_replace("^[ \n]+","",$data); if(strlen($data) > 0) { array_push($this->data,array(text => "$data")); } }
//处理指令标头 //function PIHandler($parser, $target, $data) { //}
//处理内定标头 function defaultHandler($parser, $data) { }
//处理外部实体参引标头 //function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId, $publicId) { //}
// XSL指令解析 function xsl_parser($i) { for(;$i<count($this->data);$i++) { $key = $this->data[$i]; if(isset($key[xsl])) if(eregi("/xsl",$key[xsl])) return $i; } }
// 从数据源读取数据 function get_data($ps) { if(! eregi("/",$ps)) { // 若是默认的层次 $ps = join("/",$this->datastack)."/$ps"; } return "[$ps]"; }
// 输出结果 function display() { $this->stack = array();//初始化控制栈 $this->datastack = array();//初始化数据栈
$type = true;//用于控制text项的输出 for($id=0;$id<count($this->data);$id++) { $expr = $this->data[$id]; list($key,$value) = each($expr); switch($key) { case "tag": echo "<".$value.">"; if(eregi("^/",$value)) echo "\n"; break; case "text": if($type) echo $value; break; case "xsl": //echo $value; list(,$command) = each($expr); // 取得操作集 $value = eregi_replace("[/-]","_",strtolower($value)); if(eregi("eval",$value)) $value = eregi_replace("eval","xsl_eval",$value); $this->$value($command,$id); break; } } } // 检索数据,$dsn开始的节点,$field节点名,$n匹配次数 function find($dsn,$field,$n=0) { if(! isset($dsn[Element])) return false; $root = $dsn[Element]; for($i=0;$i<count($root);$i++) { if($this->type) { if(eregi("^".$field."$",$root[$i][Name])) { if(!$n--) return $root[$i]; } }else { if(ereg("^".$field."$",$root[$i][Name])) { if(!$n--) return $root[$i]; } } } for($i=0;$i<count($root);$i++) { if($ar = $this->find($root[$i],$field,&$n)) return $ar; } return false; }
function for_each($command,&$id) { // 循环,将当前id压入堆栈 array_push($this->stack,array($id,$command[SELECT],1)); // 检索数据指针 $data = $this->find($this->dsn,$command[SELECT]); // 数据指针压入堆栈 array_push($this->datastack,$data); } function _for_each($command,&$id) { // 取得入口地址 $ar = array_pop($this->stack); // 抛弃当前数据指针 array_pop($this->datastack); // 检查是否为嵌套 if(count($this->datastack) > 0) { $dsn = array_pop($this->datastack); array_push($this->datastack,$dsn); }else $dsn = $this->dsn; $n = $ar[2]; // 检索数据指针 $data = $this->find($dsn,$ar[1],$n); if($data) { // 如检索到,则循环 $ar[2]++; array_push($this->datastack,$data); array_push($this->stack,$ar); $id = $ar[0]; } } function value_of($command) { // 取得数据指针 if(eregi("/",$command[SELECT])) { }else { if(count($this->datastack) > 0) { $dsn = array_pop($this->datastack); array_push($this->datastack,$dsn); }else $dsn = $this->dsn; $data = $this->find($dsn,$command[SELECT]); } print $data[Text]; } function _value_of() { } function stylesheet() { } function _stylesheet() { } function template($command) { echo join(" ",$command)."<br>"; } function _template() { } function apply_templates($command) { echo join(" ",$command)."<br>"; } function _apply_templates() { } function xsl_eval() { } function _xsl_eval() { } }
/**** 附录 **** 数据元素节点 Array ( [Name] // 节点名 [Text] [Attribute] [Namespace] [Comment] [ProcessingInstruction] [Element] => Array() ) *************/ ?>
|