It makes the development and maintenance of Web systems more convenient, thus effectively saving manpower and material resources, attracting more and more enterprises.
The template engine is an important method for establishing the MVC model. developers can design a set of labels that give meanings and extract data logic processing from the Interface Template effectively through technical parsing, by interpreting the meaning of the tag, the control is submitted to the corresponding business logic processing program to obtain the required data and present it as a template design, allows the designer to focus more on the presentation form. The following is my understanding and design of the template engine:
The template engine is actually the process of interpreting template data (personal opinion ^ ). Based on my understanding of website construction, the website display form is simply summarized into single and multiple forms, so we can set two corresponding tags (such as data and list) to deal with these two situations, the key point is to solve the problem of multi-layer nesting of the two labels, which is basically suitable for implementing the 80% interface form.
There are many methods to interpret templates, including string processing (to solve Nesting is a little complicated) and regular expressions. Here I use a regular expression. below is my solution (this article only provides ideas and reference code, and may not be used directly ).
Template File Parsing class:
Copy codeThe Code is as follows:
<? Php
/*
* Class: Template parsing class
* Author: 51JS. COM-ZMM
* Date: 2011.3.1
* Email: 304924248@qq.com
* Blog: http://www.cnblogs.com/cnzmm/
*/
Class Template {
Public $ html, $ vars, $ bTag, $ eTag;
Public $ bFlag = '{', $ eFlag = '}', $ pfix = 'zmm :';
Private $ folder, $ file;
Function _ construct ($ vars = array ()){
! Empty ($ vars) & $ this-> vars = $ vars;
! Empty ($ GLOBALS ['cfg _ tag_prefix']) &
$ This-> pfix = $ GLOBALS ['cfg _ tag_prefix']. ':';
$ This-> bTag = $ this-> bFlag. $ this-> pfix;
$ This-> eTag = $ this-> bFlag. '\/'. $ this-> pfix;
Empty (Tags: $ vars) & Tags: $ vars = & $ this-> vars;
}
Public function LoadTpl ($ tpl ){
$ This-> file = $ this-> GetTplPath ($ tpl );
Tags: $ file = & $ this-> file;
If (is_file ($ this-> file )){
If ($ this-> GetTplHtml ()){
$ This-> SetTplTags ();
} Else {
Exit ('template file loading failed! ');
}
} Else {
Exit ('template file ['. $ this-> file.'] does not exist! ');
}
}
Private function GetTplPath ($ tpl ){
$ This-> folder = WEBSITE_DIRROOT.
$ GLOBALS ['cfg _ tpl_root '];
Return $ this-> folder. '/'. $ tpl;
}
Private function GetTplHtml (){
$ Html = self: FmtTplHtml (file_get_contents ($ this-> file ));
If (! Empty ($ html )){
$ CallFunc = Tags: $ prefix. 'syntax ';
$ This-> html = Tags: $ callFunc ($ html, new Template ());
} Else {
Exit ('template file content is blank! ');
} Return true;
}
Static public function FmtTplHtml ($ html ){
Return preg_replace ('/(\ r) | (\ n) | (\ t) | (\ s {2,})/is', '', $ html );
}
Public function Register ($ vars = array ()){
If (is_array ($ vars )){
$ This-> vars = $ vars;
Tags: $ vars = & $ this-> vars;
}
}
Public function Display ($ bool = false, $ name = "", $ time = 0 ){
If (! Empty ($ this-> html )){
If ($ bool &&! Empty ($ name )){
If (! Is_int ($ time) $ time = 600;
$ Cache = new Cache ($ time );
$ Cache-> Set ($ name, $ this-> html );
}
Echo $ this-> html; flush ();
} Else {
Exit ('template file content is blank! ');
}
}
Public function SetAssign ($ souc, $ info ){
If (! Empty ($ this-> html )){
$ This-> html = str_ireplace ($ souc, self: FmtTplHtml ($ info), $ this-> html );
} Else {
Exit ('template file content is blank! ');
}
}
Private function SetTplTags (){
$ This-> SetPanelTags (); $ this-> SetTrunkTags (); $ this-> RegHatchVars ();
}
Private function SetPanelTags (){
$ Rule = $ this-> bTag. '([^'. $ this-> eFlag. '] +) \/'. $ this-> eFlag;
Preg_match_all ('/'. $ rule. '/ism', $ this-> html, $ out_matches );
$ This-> TransTag ($ out_matches, 'panel '); unset ($ out_matches );
}
Private function SetTrunkTags (){
$ Rule = $ this-> bTag. '(\ w +) \ s * ([^'. $ this-> eFlag. '] *?) '. $ This-> eFlag.
'((? :(?! '. $ This-> bTag.') [\ S \ s] *? | (? R) *) '. $ this-> eTag.' \ 1 \ s * '. $ this-> eFlag;
Preg_match_all ('/'. $ rule. '/ism', $ this-> html, $ out_matches );
$ This-> TransTag ($ out_matches, 'trunk'); unset ($ out_matches );
}
Private function TransTag ($ result, $ type ){
If (! Empty ($ result [0]) {
Switch ($ type ){
Case 'panel ':{
For ($ I = 0; $ I <count ($ result [0]); $ I ++ ){
$ StrTag = explode ('', $ result [1] [$ I], 2 );
If (strpos ($ strTag [0], '.') {
$ ItemArg = explode ('.', $ result [1] [$ I], 2 );
$ CallFunc = Tags: $ prefix. ucfirst ($ itemArg [0]);
If (method_exists ('tags', $ callFunc )){
$ Html = Tags: $ callFunc (chop ($ itemArg [1]);
If ($ html! = False ){
$ This-> html = str_ireplace ($ result [0] [$ I], $ html, $ this-> html );
}
}
} Else {
$ Rule = '^ ([^ \ s] +) \ s * ([\ S \ s] +) $ ';
Preg_match_all ('/'. $ rule. '/is', trim ($ result [1] [$ I]), $ tmp_matches );
$ CallFunc = Tags: $ prefix. ucfirst ($ tmp_matches [1] [0]);
If (method_exists ('tags', $ callFunc )){
$ Html = Tags: $ callFunc ($ tmp_matches [2] [0]);
If ($ html! = False ){
$ This-> html = str_ireplace ($ result [0] [$ I], $ html, $ this-> html );
}
} Unset ($ tmp_matches );
}
} Break;
}
Case 'trunk ':{
For ($ I = 0; $ I <count ($ result [0]); $ I ++ ){
$ CallFunc = Tags: $ prefix. ucfirst ($ result [1] [$ I]);
If (method_exists ('tags', $ callFunc )){
$ Html = Tags: $ callFunc ($ result [2] [$ I], $ result [3] [$ I]);
$ This-> html = str_ireplace ($ result [0] [$ I], $ html, $ this-> html );
}
} Break;
}
Default: break;
}
} Else {
Return false;
}
}
Private function RegHatchVars (){
$ This-> SetPanelTags ();
}
Function _ destruct (){}
}
?>
Tag Parsing: (currently, the data and list tags are provided for parsing)
Copy codeThe Code is as follows:
<? Php
/*
* Class: Tag Parsing class
* Author: 51JS. COM-ZMM
* Date: 2011.3.2
* Email: 304924248@qq.com
* Blog: http://www.cnblogs.com/cnzmm/
*/
Class Tags {
Static private $ attrs = null;
Static public $ file, $ vars, $ rule, $ prefix = 'tag _';
Static public function TAG_Syntax ($ html, $ that ){
$ Rule = $ that-> bTag. 'If \ s + ([^ '. $ that-> eFlag.'] +) \ s * '. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php if (\ 1) {?> ', $ Html );
$ Rule = $ that-> bTag. 'elseif \ s + ([^ '. $ that-> eFlag.'] +) \ s * '. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php} elseif (\ 1) {?> ', $ Html );
$ Rule = $ that-> bTag. 'else \ s * '. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php} else {?> ', $ Html );
$ Rule = $ that-> bTag. 'loop \ s + (\ S +) \ s * '. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php foreach (\ 1 as \ 2) {?> ', $ Html );
$ Rule = $ that-> bTag. 'loop \ s + (\ S +) \ s *'. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php foreach (\ 1 as \ 2 = >\\ 3) {?> ', $ Html );
$ Rule = $ that-> eTag. '(if | loop) \ s *'. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php }?> ', $ Html );
$ Rule = $ that-> bTag. 'php \ s * '. $ that-> eFlag .'((? :(?! '.
$ That-> bTag. ') [\ S \ s] *? | (? R) *) '. $ that-> eTag. 'php \ s *'. $ that-> eFlag;
$ Html = preg_replace ('/'. $ rule. '/ism', '<? Php \ 1?> ', $ Html );
Return self: TAG_Execute ($ html );
}
Static public function TAG_List ($ attr, $ html ){
If (! Empty ($ html )){
If (self: TAG_HaveTag ($ html )){
Return self: TAG_DealTag ($ attr, $ html, true );
} Else {
Return self: TAG_GetData ($ attr, $ html, true );
}
} Else {
Exit (the content of 'tag {list} is blank! ');
}
}
Static public function TAG_Data ($ attr, $ html ){
If (! Empty ($ html )){
If (self: TAG_HaveTag ($ html )){
Return self: TAG_DealTag ($ attr, $ html, false );
} Else {
Return self: TAG_GetData ($ attr, $ html, false );
}
} Else {
Exit ('tag {data} content is blank! ');
}
}
Static public function TAG_Execute ($ html ){
Ob_clean (); ob_start ();
If (! Empty (self: $ vars )){
Is_array (self ::$ vars )&&
Extract (self: $ vars, EXTR_OVERWRITE );
}
$ File_inc = WEBSITE_DIRINC. '/buffer /'.
Md5 (uniqid (rand (), true). '. php ';
If ($ fp = fopen ($ file_inc, 'xb ')){
Fwrite ($ fp, $ html );
If (fclose ($ fp )){
Include ($ file_inc );
$ Html = ob_get_contents ();
} Unset ($ fp );
} Else {
Exit ('template parsing file generation failed! ');
} Ob_end_clean (); @ unlink ($ file_inc );
Return $ html;
}
Static private function TAG_HaveTag ($ html ){
$ Bool_has = false;
$ Tpl_ins = new Template ();
Self: $ rule = $ tpl_ins-> bTag. '([^'. $ tpl_ins-> eFlag. '] +) \/'. $ tpl_ins-> eFlag;
$ Bool_has = $ bool_has | preg_match ('/'. self: $ rule. '/ism', $ html );
Self: $ rule = $ tpl_ins-> bTag. '(\ w +) \ s * ([^'. $ tpl_ins-> eFlag. '] *?) '. $ Tpl_ins-> eFlag.
'((? :(?! '. $ Tpl_ins-> bTag.') [\ S \ s] *? | (? R) *) '. $ tpl_ins-> eTag.' \ 1 \ s * '. $ tpl_ins-> eFlag;
$ Bool_has = $ bool_has | preg_match ('/'. self: $ rule. '/ism', $ html );
Unset ($ tpl_ins );
Return $ bool_has;
}
Static private function TAG_DealTag ($ attr, $ html, $ list ){
Preg_match_all ('/'. self: $ rule. '/ism', $ html, $ out_matches );
If (! Empty ($ out_matches [0]) {
$ Child_node = array ();
For ($ I = 0; $ I <count ($ out_matches [0]); $ I ++ ){
$ Child_node [] = $ out_matches [3] [$ I];
$ Html = str_ireplace ($ out_matches [3] [$ I], '{--> child_node _'. $ I. '<--}', $ html );
}
$ Html = self: TAG_GetData ($ attr, $ html, $ list );
For ($ I = 0; $ I <count ($ out_matches [0]); $ I ++ ){
$ Html = str_ireplace ('{--> child_node _'. $ I. '<--}', $ child_node [$ I], $ html );
}
Preg_match_all ('/'. self: $ rule. '/ism', $ html, $ tmp_matches );
If (! Empty ($ tmp_matches [0]) {
For ($ I = 0; $ I <count ($ tmp_matches [0]); $ I ++ ){
$ CallFunc = self: $ prefix. ucfirst ($ tmp_matches [1] [$ I]);
If (method_exists ('tags', $ callFunc )){
$ Temp = self: $ callFunc ($ tmp_matches [2] [$ I], $ tmp_matches [3] [$ I]);
$ Html = str_ireplace ($ tmp_matches [0] [$ I], $ temp, $ html );
}
}
}
Unset ($ tmp_matches );
}
Unset ($ out_matches); return $ html;
}
Static private function TAG_GetData ($ attr, $ html, $ list = false ){
If (! Empty ($ attr )){
$ Attr_ins = new Attbt ($ attr );
$ Attr_arr = $ attr_ins-> attrs;
If (is_array ($ attr_arr )){
Extract ($ attr_arr, EXTR_OVERWRITE );
$ Source = table_name ($ source, $ column );
$ Rule = '\ [field: \ s * (\ w +) \ s * ([^ \] *?) \ S * \/?] ';
Preg_match_all ('/'. $ rule. '/is', $ html, $ out_matches );
$ Data_str = '';
$ Data_ins = new DataSql ();
$ Attr_where = $ attr_order = '';
If (! Empty ($ where )){
$ Where = str_replace (',', 'and', $ where );
$ Attr_where = 'where'. $ where;
}
If (! Empty ($ order )){
$ Attr_order = 'ORDER BY'. $ order;
} Else {
$ Fed_name = '';
$ Fed_ins = $ data_ins-> GetFedNeedle ($ source );
$ Fed_cnt = $ data_ins-> GetFedCount ($ fed_ins );
For ($ I = 0; $ I <$ fed_cnt; $ I ++ ){
$ Fed_flag = $ data_ins-> GetFedFlag ($ fed_ins, $ I );
If (preg_match ('/auto_increment/ism', $ fed_flag )){
$ Fed_name = $ data_ins-> GetFedName ($ fed_ins, $ I );
Break;
}
}
If (! Empty ($ fed_name ))
$ Attr_order = 'ORDER BY'. $ fed_name. 'desc ';
}
If ($ list = true ){
If (empty ($ source) & empty ($ SQL )){
Exit ('tag {list} must specify the source attribute! ');
}
$ Attr_rows = $ attr_page = '';
If ($ rows> 0 ){
$ Attr_rows = 'limit 0, '. $ rows;
}
If (! Empty ($ SQL )){
$ Data_ SQL = $ SQL;
} Else {
$ Data_ SQL = 'select * from ''. $ source .'''.
$ Attr_where. $ attr_order. $ attr_rows;
}
If ($ pages = 'true '&&! Empty ($ size )){
$ Data_num = $ data_ins-> GetRecNum ($ data_ SQL );
$ Page_cnt = ceil ($ data_num/$ size );
Global $ page;
If (! Isset ($ page) | $ page <1) $ page = 1;
If ($ page> $ page_cnt) $ page = $ page_cnt;
$ Data_ SQL = 'select * from ''. $ source. '''. $ attr_where.
$ Attr_order. 'limit'. ($ page-1) * $ size. ','. $ size;
$ GLOBALS ['cfg _ page_curr '] = $ page;
$ GLOBALS ['cfg _ page_prev'] = $ page-1;
$ GLOBALS ['cfg _ page_next '] = $ page + 1;
$ GLOBALS ['cfg _ page_nums '] = $ page_cnt;
If (function_exists ('list _ pagelink ')){
$ GLOBALS ['cfg _ page_list '] = list_pagelink ($ page, $ page_cnt, 2 );
}
}
$ Data_idx = 0;
$ Data_ret = $ data_ins-> SqlCmdExec ($ data_ SQL );
While ($ row = $ data_ins-> GetRecArr ($ data_ret )){
If ($ skip> 0 &&! Empty ($ flag )){
$ Data_idx! = 0 &&
$ Data_idx % $ skip = 0 &&
$ Data_str. = $ flag;
}
$ Data_tmp = $ html;
$ Data_tmp = str_ireplace ('@ idx', $ data_idx, $ data_tmp );
For ($ I = 0; $ I <count ($ out_matches [0]); $ I ++ ){
$ Data_tmp = str_ireplace ($ out_matches [0] [$ I],
$ Row [$ out_matches [1] [$ I], $ data_tmp );
}
$ Data_str. = $ data_tmp; $ data_idx ++;
}
} Else {
If (empty ($ source )){
Exit ('tag {data} must specify the source attribute! ');
}
$ Data_ SQL = 'select * from ''. $ source.
'''. $ Attr_where. $ attr_order;
$ Row = $ data_ins-> GetOneRec ($ data_ SQL );
If (is_array ($ row )){
$ Data_tmp = $ html;
For ($ I = 0; $ I <count ($ out_matches [0]); $ I ++ ){
$ Data_val = $ row [$ out_matches [1] [$ I];
If (empty ($ out_matches [2] [$ I]) {
$ Data_tmp = str_ireplace ($ out_matches [0] [$ I], $ data_val, $ data_tmp );
} Else {
$ Attr_str = $ out_matches [2] [$ I];
$ Attr_ins = new Attbt ($ attr_str );
$ Func_txt = $ attr_ins-> attrs ['function'];
If (! Empty ($ func_txt )){
$ Func_tmp = explode (', $ func_txt );
If (function_exists ($ func_tmp [0]) {
Eval ('$ func_ret ='. str_ireplace ('@ me ',
'\ ''. $ Data_val.' \'', $ func_txt ));
$ Data_tmp = str_ireplace ($ out_matches [0] [$ I], $ func_ret, $ data_tmp );
} Else {
Exit ('a nonexistent function is called! ');
}
} Else {
Exit ('tag setting property is invalid! ');
}
}
}
$ Data_str. = $ data_tmp;
}
}
Unset ($ data_ins );
Return $ data_str;
} Else {
Exit ('tag setting property is invalid! ');
}
} Else {
Exit ('No tag property is set! ');
}
}
Static public function _ callStatic ($ name, $ args ){
Exit ('tag {'. $ name.'} does not exist! ');
}
}
?>