用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() ) *************/ ?>
|