ThinkPHP模板多层继承

      帮朋友用ThinkPHP写一个网站,最近就碰到ThinkPHP模板不能多层继承的问题,就跟这里讲的问题一样:http://www.thinkphp.cn/topic/26853.html

      比如base.html是这样:

  1. ...base内容1...
  2. <block name="content"></block>
  3. ...base内容2...
     a.html继承base.html:
  1. <extend name="base" />
  2.  <block name="content">
  3. ....a内容1...
  4.  <block name="b_content"></block>
  5. ....a内容2...
  6.  </block>

     现在b.html想继承a.html

  1. <extend name="a" />
  2.  <block name="b_content">
  3. ...b内容1...
  4.  </block>
     这样b.html就可以共用a.html和base.html模板了,但是ThinkPHP不给力啊,这样做之后输出如下:

  1. <extend name="base" />
  2. ....a内容1...
  3. ...b内容1...
  4. ....a内容2...
它并没有解析a.html中的extend标签,悲剧啊,它不支持,我就看看能不能让它支持,研究了两天,终于搞定,写出来分享一下:
模板解析的实现代码文件是:ThinkPHP/Library/Think/Template.class.php,在这个文件里面主要关注两个函数parseExtend和replaceBlock,从函数名可以看出这两个函数分别是解析extend标签和替换block标签:
    // 解析模板中的extend标签
    protected function parseExtend($content) {
        $begin      =   $this->config['taglib_begin'];
        $end        =   $this->config['taglib_end'];        
        // 读取模板中的继承标签
        $find       =   preg_match('/'.$begin.'extend\s(.+?)\s*?\/'.$end.'/is',$content,$matches);
        if($find) {
            //替换extend标签
            $content    =   str_replace($matches[0],'',$content);
            // 记录页面中的block标签
            preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', array($this, 'parseBlock'),$content);
            // 读取继承模板
            $array      =   $this->parseXmlAttrs($matches[1]);
            $content    =   $this->parseTemplateName($array['name']);
            $content    =   $this->parseInclude($content, false); //对继承模板中的include进行分析
            // 替换block标签
            $content = $this->replaceBlock($content);
        }else{
            $content    =   preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', function($match){return stripslashes($match[2]);}, $content);
        }
        return $content;
    }
//...
    /**
     * 替换继承模板中的block标签
     * @access private
     * @param string $content  模板内容
     * @return string
     */
    private function replaceBlock($content){
        static $parse = 0;
        $begin = $this->config['taglib_begin'];
        $end   = $this->config['taglib_end'];
        $reg   = '/('.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.')(.*?)'.$begin.'\/block'.$end.'/is';
        if(is_string($content)){
            do{
                $content = preg_replace_callback($reg, array($this, 'replaceBlock'), $content);
            } while ($parse && $parse--);
            return $content;
        } elseif(is_array($content)){
            if(preg_match('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'/is', $content[3])){ //存在嵌套,进一步解析
                $parse = 1;
                $content[3] = preg_replace_callback($reg, array($this, 'replaceBlock'), "{$content[3]}{$begin}/block{$end}");
                return $content[1] . $content[3];
            } else {
                $name    = $content[2];
                $content = $content[3];
                $content = isset($this->block[$name]) ? $this->block[$name] : $content;
                return $content;
            }
        }
    }
从parseExtend函数可以看出它真的没有支持多层继承,那就加上一个递归让它支持:
    // 解析模板中的extend标签
    protected function parseExtend($content) {
        $begin      =   $this->config['taglib_begin'];
        $end        =   $this->config['taglib_end'];        
        // 读取模板中的继承标签
        $find       =   preg_match('/'.$begin.'extend\s(.+?)\s*?\/'.$end.'/is',$content,$matches);
        if($find) {
            //替换extend标签
            $content    =   str_replace($matches[0],'',$content);
            // 记录页面中的block标签
            preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', array($this, 'parseBlock'),$content);
            // 读取继承模板
            $array      =   $this->parseXmlAttrs($matches[1]);
            $content    =   $this->parseTemplateName($array['name']);
            $content    =   $this->parseInclude($content, false); //对继承模板中的include进行分析
            // 替换block标签
            $content = $this->replaceBlock($content);
            // 递归解析,支持多层继承
            $content = $this->parseExtend($content);
        }else{
            $content    =   preg_replace_callback('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'(.*?)'.$begin.'\/block'.$end.'/is', function($match){return stripslashes($match[2]);}, $content);
        }
        return $content;
    }
    这样做之后,之前的例子输出如下:
  1. ...base内容1...

  2. ...base内容2...
    还是没有达到预期结果,调试了两天才发现是replaceBlock函数里面一个数字导致的bug,修复之后如下:
    /**
     * 替换继承模板中的block标签
     * @access private
     * @param string $content  模板内容
     * @return string
     */
    private function replaceBlock($content){
        static $parse = 0;
        $begin = $this->config['taglib_begin'];
        $end   = $this->config['taglib_end'];
        $reg   = '/('.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.')(.*?)'.$begin.'\/block'.$end.'/is';
        if(is_string($content)){
            do{
                $content = preg_replace_callback($reg, array($this, 'replaceBlock'), $content);
            } while ($parse && $parse--);
            return $content;
        } elseif(is_array($content)){
            if(preg_match('/'.$begin.'block\sname=[\'"](.+?)[\'"]\s*?'.$end.'/is', $content[3])){ //存在嵌套,进一步解析
                $parse = 1;
                $content[3] = preg_replace_callback($reg, array($this, 'replaceBlock'), "{$content[3]}{$begin}/block{$end}");
                return $content[1] . $content[3];
            } else {
                $name    = $content[2];
                $content = $content[0];//就是这一行,之前的索引是3,实际应该是0
                $content = isset($this->block[$name]) ? $this->block[$name] : $content;
                return $content;
            }
        }
    }
    这么改之后,结果如下:
  1. ...base内容1...
  2. ....a内容1...
  3. ...b内容1...
  4. ....a内容2...
  5. ...base内容2...

     可以看出符合预期了,就这么两句代码的事,可是为什么ThinkPHP官方代码里不支持呢?


参考:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值