找回密码
 注册
搜索
查看: 3256|回复: 0

[转载]用PHP解析XSL

[复制链接]
发表于 2004-6-8 15:43:07 | 显示全部楼层 |阅读模式
在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,  "IHandler");
        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()
)
*************/
?>
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2025-5-5 06:28 , Processed in 0.015384 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表