首页 > 开发 > PHP > 正文

用PHP解析XSL

2024-05-04 23:00:22
字体:
来源:转载
供稿:网友
  • 网站运营seo文章大全
  • 提供全面的站长运营经验及seo技术!
  •     用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>[email protected]</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>[email protected]</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>[email protected]</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()
    )
    *************/
    ?>
    发表评论 共有条评论
    用户名: 密码:
    验证码: 匿名发表