Discuz 模版解析代码剥离

2013年12月10日 分类: Discuz, PHP (190 个脚步)

虽说Discuz的毛病居多,但是有些东西还是值得赞扬的。

比如说模版解析那套代码,检测到修改过模版后,程序会再次生成此文件包含的所有文件的缓存

Discuz的模板解析原理是用正则表达式替换一些模板中的规定的语言标记,然后写到cache目录中,再用include引用到index, forumdisplay等等中,和smarty的原理基本上相同,但是没有smarty强大的功能。

但是呢,我们做的是一个小小的网站,用不着去用smarty这样大型的模版框架,维护起来还很麻烦,而且,你不觉得每次都要赋值和display页面很烦么。。

我看了一下网上有人提取过出来,但是版本都已经是很老的了,所以就想在新版本把模版解析的代码剥离开来。

   function checktplrefresh($maintpl, $subtpl, $timecompare, $cachefile, $tpldir, $file) {
        if(empty($timecompare) || filemtime($subtpl) > $timecompare) {
            $this->template = new template();
            $this->template->parse_template($maintpl, 0, $tpldir, $file, $cachefile);
            unset($maintpl, $subtpl, $timecompare, $cachefile, $tpldir, $file);
            return TRUE;
        }
        return FALSE;
    }

    function display($file) {
        $oldfile    = $file;
        $tpldir     = TPL.THEME.'/';
        $filebak    = $file;
        if(!$tpldir) {
            $tpldir = TPL.THEME.'/';
        }
        $tplfile    = $tpldir.$file.'.php';
        $cachefile  = TPL_CACHE.str_replace('/', '_', THEME.'/'.$file).'.tpl.php';
        if(file_exists($cachefile)) {
            $timecompare = filemtime($cachefile);
        } else {
            $timecompare = NULL;
        }
        $this->checktplrefresh($tplfile, $tplfile, $timecompare, $cachefile, $tpldir, $file);
        unset($oldfile, $filebak, $tplfile, $tplfile, $timecompare, $tpldir, $file);
        return $cachefile;
    }

在上面的代码中,TPLTHEME为分别为你的模版路径和主题名称,你可以写在你的全局设置中

要注意的是,这里的模版路径为.tpl.php,如果你想要以html结尾,你可以自己修改这里。

下面就是模版的类了。在这里,使用了正则表达式来替换特定的代码串。

<?php
(!defined('INAPP') || INAPP !== TRUE) && die('Access deny.');

class template {

    private $subtemplates = array();
    private $replacecode = array('search' => array(), 'replace' => array());
    private $language = array();
    private $file = '';

    public function parse_template($tplfile, $templateid, $tpldir, $file, $cachefile) {
        $this->file = $file;

        if($fp = fopen($tplfile, 'r')) {
            $template = fread($fp, filesize($tplfile));
            fclose($fp);
        } else {
            $this->error('template_notfound', $tplfile);
        }

        $var_regexp = "(($[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*(->)?[a-zA-Z0-9_x7f-xff]*)([[a-zA-Z0-9_-."'[]$x7f-xff]+])*)";
        $const_regexp = "([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)";

        $headerexists = preg_match("/{(sub)?templates+[w/]+?header}/", $template);

        $template = str_ireplace("", '', $template);
        $template = preg_replace("/([nr]+)t+/s", "1", $template);
        $template = preg_replace("/{C}/s", "{1}", $template);
        $template = preg_replace("/{langs+(.+?)}/ies", "$this->languagevar('1')", $template);
        $template = preg_replace("/[nrt]*{ad/(.+?)}[nrt]*/ie", "$this->adtags('1')", $template);
        $template = preg_replace("/[nrt]*{ads+([a-zA-Z0-9_[]]+)/(.+?)}[nrt]*/ie", "$this->adtags('2', '1')", $template);
        $template = preg_replace("/[nrt]*{date((.+?))}[nrt]*/ie", "$this->datetags('1')", $template);

        $template = str_replace("{LF}", "", $template);
        $template = preg_replace("/{($[a-zA-Z0-9_->[]'"$.x7f-xff]+)}/s", "", $template);
        $template = preg_replace("/$var_regexp/es", "template::addquote('')", $template);
        $template = preg_replace("/?>/es", "$this->addquote('')", $template);

        $template = preg_replace("/[nrt]*{templates+([a-z0-9_:/]+)}[nrt]*/ies", "$this->stripvtags('')", $template);
        $template = preg_replace("/[nrt]*{templates+(.+?)}[nrt]*/ies", "$this->stripvtags('')", $template);
        $template = preg_replace("/[nrt]*{echos+(.+?)}[nrt]*/ies", "$this->stripvtags('')", $template);

        $template = preg_replace("/([nrt]*){ifs+(.+?)}([nrt]*)/ies", "$this->stripvtags('13')", $template);
        $template = preg_replace("/([nrt]*){elseifs+(.+?)}([nrt]*)/ies", "$this->stripvtags('13')", $template);
        $template = preg_replace("/{else}/i", "", $template);
        $template = preg_replace("/{/if}/i", "", $template);

        $template = preg_replace("/[nrt]*{loops+(S+)s+(S+)}[nrt]*/ies", "$this->stripvtags('')", $template);
        $template = preg_replace("/[nrt]*{loops+(S+)s+(S+)s+(S+)}[nrt]*/ies", "$this->stripvtags(' 3) { ?>')", $template);
        $template = preg_replace("/{/loop}/i", "", $template);

        $template = preg_replace("/{$const_regexp}/s", "", $template);
        if(!empty($this->replacecode)) {
            $template = str_replace($this->replacecode['search'], $this->replacecode['replace'], $template);
        }
        $template = preg_replace("/ ?>[nr]*transamp('')", $template);
        $template = preg_replace("/]*?src="(.+?)"(.*?)>s*/ies", "$this->stripscriptamp('1', '2')", $template);
        $template = preg_replace("//is", "", $template);

        $headeradd = '';
        $this->subtemplates = array();
        for($i = 1; $i <= 3; $i++) {
            if($this->strexists($template, '{subtemplate')) {
                $template = preg_replace("/[nrt]*({C})?[nrt]*/ies", "$this->loadsubtemplate('2')", $template);
            }
        }
        if(!empty($this->subtemplates)) {
            $headeradd .= "n0n";
            foreach($this->subtemplates as $fname) {
                $headeradd .= '|| $this->checktplrefresh("'.$tplfile.'", "'.$fname.'", '.time().', "'.$cachefile.'", "'.$tpldir.'", "'.$file.'")'."n";
            }
            $headeradd .= ';';
        }
        $template = "n$template";

        if(!$fp = fopen($cachefile, 'w')) {
            $this->error('directory_notfound', dirname($cachefile));
        }
        flock($fp, 2);
        fwrite($fp, $template);
        fclose($fp);
    }

    private function languagevar($var) {
        if(isset($langvar[$var])) {
            return $langvar[$var];
        } else {
            return '!'.$var.'!';
        }
    }

    private function adtags($parameter, $varname = '') {
        $parameter = stripslashes($parameter);
        $i = count($this->replacecode['search']);
        $this->replacecode['search'][$i] = $search = "{C}";
        $this->replacecode['replace'][$i] = "";
        return $search;
    }

    private function datetags($parameter) {
        $parameter = stripslashes($parameter);
        $i = count($this->replacecode['search']);
        $this->replacecode['search'][$i] = $search = "{C}";
        $this->replacecode['replace'][$i] = "";
        return $search;
    }

    private function stripphpcode($type, $code) {
        $this->phpcode[$type][] = $code;
        return '{phpcode:'.$type.'/'.(count($this->phpcode[$type]) - 1).'}';
    }

    private function loadsubtemplate($file) {
        $tplfile = $this->display($file, 0, '', 1);
        $filename = $tplfile;
        if(($content = implode('', file($filename))) || ($content = $this->getphptemplate(implode('', file(substr($filename, 0, -4).'.php'))))) {
            $this->subtemplates[] = $tplfile;
            return $content;
        } else {
            return '';
        }
    }

    private function getphptemplate($content) {
        $pos = strpos($content, "n");
        return $pos !== false ? substr($content, $pos + 1) : $content;
    }

    private function transamp($str) {
        $str = str_replace('&', '&', $str);
        $str = str_replace('&amp;', '&', $str);
        $str = str_replace('"', '"', $str);
        return $str;
    }

    private function addquote($var) {
        return str_replace(""", """, preg_replace("/[([a-zA-Z0-9_-.x7f-xff]+)]/s", "['1']", $var));
    }


    private function stripvtags($expr, $statement = '') {
        $expr = str_replace(""", """, preg_replace("//s", "1", $expr));
        $statement = str_replace(""", """, $statement);
        return $expr.$statement;
    }

    private function stripscriptamp($s, $extra) {
        $extra = str_replace('"', '"', $extra);
        $s = str_replace('&', '&', $s);
        return "";
    }

    private function strexists($string, $find) {
        return !(strpos($string, $find) === FALSE);
    }

    private function error($message, $tplname) {
        exit($message);
    }
}

以上代码已经能正常使用,模版的话,请参考一下discuz的模版的写法。都是一样的。

不过要注意的是,我去掉了一些比如钩子、diy相关的东西。这些东西一般都不会用得到,所以代码的量就减少了很多。

剩下的,就自己组织把。记得cache目录需要写入权限~~

Discuz 模版解析代码剥离 【声明】本文 Discuz 模版解析代码剥离 为柠之漠然原创文章,转载请注明出自 枫之落叶
并保留本文有效链接:https://blog.shiniv.com/2013/12/discuz-template-separation/ , 转载请保留本声明!

标签: , , , ,
目前还没有任何评论.
你必须要启用 Javascript