<p> 前段时间,一直比较关心php的模板技术的我,接到了客户的要求:全站利用模板技术,可以控制多模板,这个项目要求有强大的后台,主要是多模板,难点就在模板的导入和识别并且生成上,我仔细考察了php的模板核心技术,无外乎查找字符串开始,然后定义替换变量,替换成数据,然后并成输出页输出,牵扯到的缓存我就不再说了.</p><p> 我们来看看这种方式的生成方式的缺点:如果要"多样式"的显示数据,当然我先不说CSS,不能真正的把数据和表示分离,无外乎利用css样式表方式显示而已,我们现在接到的这个项目不但要求css样式表的可选化,而且要求数据显示的"多方式",打个比方,看下图:</p><p> <img src="/content/uploadfile/200805/2008053117441694.gif" onclick="get_larger(this)" alt="PHP模板用xml的思路" /></p><p> 我们下面为了容易描述期间,我这样表示各个部分:A,B,C,D,E,F,G这6个区可以表示数据显示区域,从设计者角度考虑,有全局(div控制A-G),TOP(A),LEFT(B),RIGHT(C,D,E,F)和FOOTER(G),当然,您可以分的更细一些,这么显示,可以用div控制的,但这不是模板技术,请明白,多样式表不叫模板,这和模板无关,所谓的模板,就是只与"数据布局"相关,在A数据我们另外可以表示成一个导航,如果您喜欢的话.这在传统的模板技术中会这样写:</p><code>...<br /><div id="top"><br />{$SITE_TOP$}//cjjer制作<br /></div><br />...</code><p> 替换的时候replace()的是{$ 和$}符号中的变量,这在数据简单的时候,比方只是一个导航,而我们如果要的是一个很复杂的数据显示的时候,那就很难控制了,因为把一个很大量输出数据放在一个变量中很难保证不出错.</p><p> 另外,传统的模板(在php中)是这样的,获取模板的文件,加载,显示,这没什么问题,问题就在当web项目(不完全是网站)非常复杂的时候,很容易替换错误和模板单调,虽然可以用css控制一些显示,但很难控制数据的布局,比方,E区我今天不想要了,你改模板,重新加载文件... ?</p>
<p> </p>
<p> 有没有更加容易的解决方案?有.</p><p> 我提出了这样一种观点:</p><p> 模板页为 XML文档 ,模板节点加载已有模块 ,加载"仿xml数据"生成文件 (原谅我,这么说我觉得已经非常容易了)下面我就这种模板技术详谈.</p><p> 先看看我说的模块是什么东西,大家都知道,html中的<div>呀,<table>都是显示数据布局的一些布局标签,为什么我们不能自己制作这种标签呢?例如:我现在"创"一个这样的标签<format>这里,当然,这个标签对项目是有意义的,表示控制全局的模块节点,如果在模板页中出现节点<format>加载的就是对应名称为 format 的模块数据:</p><code><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"><br /><HTML xmlns="http://www.w3.org/1999/xhtml"><br /><head><br /><title>[%TITLE%]</title><br /><meta http-equiv="content-type" content="text/html; charset=gb2312" /><br /><meta http-equiv="Content-Language" content="zh-CN" /><br /><meta name="author" content="[%AUTHOR%]" /><br /><meta name="copyright" content="[%COPYRIGHT%]" /><br /><meta name="description" content="[%DESCRIPTION%]" /><br /><meta name="keywords" content="[%KEYWORDS%]" /><br /><link href="styles/[%STYLES%]/import.css" rel="stylesheet" type="text/css" /><br /></head><br /><body><br /><div id="format"><br />&%format%&<br /></div><br /></body><br /></html></code><p> 这里我把这个模板节点定义成 加载控制全局的html源了,再看这个标签topdata:</p>
<p> </p>
<p> <div id="topdata">{%TOP_MESS%}<%=show_top_meun()%></div></p><p> 如果这个节点被加载,生成的文件里面会将topdata节点替换成如上的html文档模板,这就是模块</p><p> 这里的模块也可以是xml文档,重复加载模块,也可以是终数据.</p><p> 当这些还有模块节点的xml模板被加载以后,就被程序识别,对应的加载成html二级模块,然后提出我们的"仿xml 数据"标记中的数据,正则替换对应的节点,生成文件,这里的"仿xml数据"是这种方式的数据:</p><code>{%TITLE%}<%=cjjer_hometitle%>{%/TITLE%}<br />{%STYLE%}default{%/STYLE%}<br />{%site_top%}<%=get_cache(0)%>{%/site_top%}<br />{%format_two%}<div id="footer_ul"><%Call light()%></div><br />{%/format_two%}<br />{%site_footer%}<%Call cc_footer()%>{%/site_footer%}</code><p> 这里,您可能马上理解了我说的"仿xml数据"了,这种加载数据的方式也是xml分析节点,然后直接正则替换,当然可以include文件的(asp,php).</p><p> 好了,现在您想必概念已经很清楚了,(不清楚的话重新看上面的话,或看如下的例子)</p><p> 我就举个举个简单的例子说明一下(format_index.xml):</p><code><format><br /><site_top>{%site_top%}</site_top><br /><format_two><br /><home_bigflash>{%home_bigflash%}</home_bigflash><br />{%format_two%}<br /></format_two><br /><site_footer>{%site_footer%}</site_footer><br /></format></code><p> 模块:</p><code>//format,就是最上面的那个,不列举了<br />//home_bigflash<br /><div id="main_img"><br /> <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/<br />swflash.cab#version=6,0,29,0" width="100%"><br /><param name="movie" value="images/main.swf"><br /><param name="wmode" value="transparent"><br /><param name="quality" value="high"><br /><embed src="images/main.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="100%"></embed><br /></object><br /></div><br />//format_two<br /><div id="format_two">(&format_two&)<script language="javascript" type="text/javascript" src="js/same_h2.js"></script> </div><br />//site_footer<br /><div id="site_footer">(&site_footer&)</div></code><p> 差不多应该加载的模板和模块就这点吧(都是可以重用的.)</p>
<p> </p>
<p> 现在是程序处理:</p><code><%<br />'根据输入判断输出是名称还是数据<br />Function geturlxml(outfile,mode)<br />geturlxml=False<br />If outfile="" Then Exit Function<br />Dim tab<br />Select Case killint(mode,0,0,2)<br />Case 0:tab="szd_tpl"<br />Case 1:tab="szd_content"<br />End Select<br />Set rs=conn.execute("select ["&tab&"] FROM szd_asp where [szd_link]='"&outfile&"'")<br />If rs.eof And rs.bof Then<br />Exit Function<br />else<br />geturlxml=rs(tab)<br />End If<br />End Function<br />'提取include文件到原始的数据项中<br />Function getincludefile(x)<br />Dim regxml,html<br />html=""<br />Set regxml=new regexp<br />regxml.ignorecase=True<br />regxml.global=True<br />regxml.pattern="(<!--#include)(s)(file|virtual)(=)(u0022)([a-zA-Z][A-Za-z0-9_/]{0,30})(.)(asp|inc|dll)(u0022)(-->)"<br />Dim Matches,Match<br />Set Matches = regxml.Execute(x)<br />For Each Match in Matches<br />html=html&match.value<br />Next<br />getincludefile=html<br />End Function<br />'获取模板项,先判断输出,返回值是 html 形式的模板,参数是模板的名称<br />Function gettpl(url)<br />Dim mudule_file,xml_file,asp_file<br />xml_file=url<br />asp_file=gettpldata(xml_file,0)'由模板名称获取模板的内容<br />Dim loadxml,parsexml,nodes,node<br />Set loadxml = Server.CreateObject("MSxml2.DOMDocument")<br />loadxml.async=false<br />parsexml=server.mappath("../"&gettpldata(xml_file,10)&xml_file)'装载本地的xml文档,先到数据库取到路径<br />loadxml.load parsexml<br />Set nodes = loadxml.selectNodes("//*")<br />for each node in nodes<br />asp_file=insertaspinhtml(asp_file,gettpldata(node.nodename,1),node.nodename)'加载模块到模板,输出html模板<br />Next<br />gettpl=asp_file<br />End function<br />'匹配模板中的数据项<br />'把data中的数据传入html文件当中去<br />Function tpltodata(html,data)<br />Dim regxml<br />Set regxml=new regexp<br />regxml.ignorecase=True<br />regxml.global=True<br />regxml.pattern="({%)([a-zA-Z][A-Za-z0-9_]{2,60})(%})(.[^[]*)({%/)2(%})"<br />Dim Matches,Match<br />Set Matches = regxml.Execute(data)<br />For Each Match in Matches<br />html=Replace(html,"{%"®xml.Replace(match.value,"$2")&"%}",regxml.Replace(match.value,"$4"))<br />Next<br />tpltodata=html<br />End Function<br />'geturlxml 传入参数模板名称,返回模板的内容,失败的时候返回 null<br />Function geturlxml(outfile)<br />geturlxml=Null<br />If outfile="" Then Exit Function<br />Set rs=conn.execute("select [szd_tpl] FROM szd_asp where [szd_link]='"&outfile&"'")<br />If rs.eof And rs.bof Then<br />Exit Function<br />else<br />geturlxml=rs("szd_tpl")<br />End If<br />End Function<br />public Function gettpldata(url,mode)<br />'Set rs=server.CreateObject("server.adodbrecordset")<br />If Len(url)<1 Then Exit Function<br />Dim szd_keytpl<br />Select Case mode<br />Case 0:Set rs=conn.execute("select [szd_content] from szd_tpl where [szd_link]='"&url&"'")<br />Case 10:Set rs=conn.execute("select [szd_url] from szd_tpl where [szd_link]='"&url&"'")<br />Case 1:Set rs=conn.execute("select [szd_content] from szd_keytpl where [szd_key]='"&url&"'")<br />End Select<br />If Not(rs.eof And rs.bof) Then<br />szd_keytpl=rs(0)<br />Else<br />szd_keytpl="<div>{%nodefined%}</div>"<br />End If<br />gettpldata=szd_keytpl<br />End Function<br />'加载标记的模块到 xml 格式的模板中,输出html格式的模板,asp:xml格式模板,html:导入的模块,xmls:当前标记<br />Function insertaspinhtml(asp,html,xmls)<br />Dim regxml,temp,temps,tempasp,tempasp_e<br />Set regxml=new regexp<br />regxml.ignorecase=True<br />regxml.global=True<br />regxml.pattern="(<"&xmls&">)(.[^[]*)(</"&xmls&">)"<br />temp=regxml.Replace(asp,"$2")<br />Dim Matches,Match<br />Set Matches = regxml.Execute(asp)<br />For Each Match in Matches<br />html=Replace(html,"(&"&xmls&"&)",regxml.Replace(match.value,"$2"))<br />asp=Replace(asp,Match.Value,html)<br />next<br />temps=Replace(html,"(&"&xmls&"&)",temp)<br />insertaspinhtml=asp<br />End Function<br />Function update_aspfile(id)<br />Dim update_aspfile_return<br />update_aspfile_return=False<br />id=killint(id,0,0,14)<br />If id=0 Then Exit Function<br />Set rs=conn.execute("select [szd_level],[szd_content],[szd_tpl],[szd_link] from szd_asp where id="&id&"")<br />If rs.eof And rs.bof Then Exit Function<br />Dim tpl_level,tpl_content,tpl_tpl,html_content,asp_file_now<br />tpl_level=rs(0)<br />tpl_content=rs(1)<br />tpl_tpl=rs(2)<br />asp_file_now=rs(3)<br />If Len(tpl_tpl)<4 Or tpl_level=1 Or IsNull(tpl_tpl) Then<br />html_content=tpl_content<br />Else<br />html_content=gettpl(tpl_tpl)<br /> If Not isnull(html_content) Then<br /> html_content=getincludefile(tpl_content)&tpltodata(html_content,tpl_content)<br /> 'response.write("<hr/>"&server.htmlencode(getincludefile(tpl_content))&"<hr/>")<br /> Else<br /> html_content=tpl_content<br /> 'response.write("<hr/>"&server.htmlencode(html_content)&"<hr/>")<br /> End If<br />End If<br />If writeto("../",asp_file_now,html_content,2) Then update_aspfile_return=True<br />update_aspfile=update_aspfile_return<br />End Function<br />%></code><p> 生成文件:</p>
<p> </p>
<code><!--#include file="inc/setup.asp"--><!--#include file="inc/cjjerfunction.asp"--><!--#include file="inc/light.asp"--><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd"><br /><HTML xmlns="http://www.w3.org/1999/xhtml"><br /><head><br /><title><%=cjjer_hometitle%></title><br /><meta http-equiv="content-type" content="text/html; charset=gb2312" /><br /><meta http-equiv="Content-Language" content="zh-CN" /><br /><meta name="author" content="老农" /><br /><meta name="copyright" content="环境工程系" /><br /><meta name="description" content="<%=cjjer_hometitle%>" /><br /><meta name="keywords" content="<%=cjjer_hometitle%>" /><br /><link href="styles/default/import.css" rel="stylesheet" type="text/css" /><br /></head><br /><body><br /><div id="format"><br /><div id="site_top"></div><br /><div id="top_ul"><%=get_cache(0)%></div><br /><div id="format_two"><br /><div id="main_img"><br /> <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" width="100%"><br /><param name="movie" value="images/main.swf"><br /><param name="wmode" value="transparent"><br /><param name="quality" value="high"><br /><embed src="images/main.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="100%"></embed><br /></object><br /></div><br /><div id="footer_ul"><%Call light()%></div><br /></div><br /><div id="site_footer"><%Call cc_footer()%></div><br /></div><br /></body><br /></html></code><p> 这里,我的模块用了div,是利于样式表的使用.</p></p><p> 主要思路如图:</p><p> <img src="/content/uploadfile/200805/2008053117441661.gif" onclick="get_larger(this)" alt="PHP模板用xml的思路" /></p><p> 其他的就不再说了,不知道说明白了.</p></p>