框架说明:
1.框架还没有完全开发好,完善了模板解析的主要功能,不过我在这里会不定期的更新直至整个框架的完成。
2.框架统一采用Javascript脚本语言开发,因为本人还是不习惯VBScript,实在抱歉。
3.框架采用MVC模式,动态生成缓存文件以供系统调用。
4.框架借鉴了PHP框架实现思路。
语法说明:
1.不再使用关键字var来定义变量了,而是采用$来直接定义变量,比如$sitename,目的是和客户端有所区分。
2.常量采用全大写的格式,比如$_SGLOBAL
3.系统频繁采用静态对象,比如$_SGLOBAL,$_SC,$_SCONFIG...目的是模拟PHP的关联数组,个人感觉它非常好用且结构清晰。
好了,暂时就这些,以后如有其它约定还会在这里补充的。
MVC系统一定有它的框架结构,我的框架结构如下:
./
admin - 后台管理目录
data - 系统缓存,数据库备份目录
/tpl_cache 模板生成的缓存都在这里了
image - 图像目录
source - 核心文件目录
template - 模板目录
/default 系统默认模板都在这里了(这个是必须要有的目录)
/image 模板相关的图像目录
/blue 自定义模板目录(自己都可以随意定义,可能还有好多其他的模板目录)
/...(其它的一些自定义模板目录就不一一举例了,具体格式可以参照default模板)
common.asp 网站公共文件
config.asp 网站配置文件
index.asp 网站入口文件
好,框架结构定下来了,不会有大的变动,只会有很细微的变动,也会在这里随时更新的。
下面我先把完成的一些代码贴出来。
common.asp - 网站公共文件
<%@LANGUAGE="JAVASCRIPT" CODEPAGE="936"%>
<%
IN_RICHMOND = true;
X_VER = '1.0';
X_RELEASE = '20090401';
FSO = Server.CreateObject('Scripting.FileSystemObject');
XMLHTTP = Server.CreateObject("Microsoft.XMLHTTP");
STREAM = Server.CreateObject("ADODB.STREAM");
$_SGLOBAL = {
};
%>
<!--#include file = './config.asp'-->
<!--#include file = './source/function_common.asp'-->
<!--#include file = './source/function_template.asp'-->
<!--#include file = './source/class_database.asp'-->
<!--#include file = './source/class_data_grid.asp'-->
<%
//这里添加更多代码
%>
大家可以看到,首先是定义了网站的一些常量,其次包含了一些核心文件,比如公共函数库、模板、数据库、分页,后续可能还会加入更多这样的类库。
config.asp - 网站配置文件
<%
/**
* page build by Richmond 09-04-06
*/
//配置参数
$_SC = {
dbhost: '(local)',//服务器地址
dbuser: 'Richmond',//用户
dbpw: '******',//密码
dbname: 'database'//数据库
//...
};
$_SCONFIG = {
template: 'default'
//...
};
%>
配置文件就是定义静态对象来存储了。
./source/function_common.asp 网站公共函数库(后续会不断的更新)
<%
/**
* page build by Richmond 09-04-06
*/
//测字符串实际长度
String.prototype.tlength = function(){
$arr = this.match(/[^\x00-\xff]/ig);
return this.length + ($arr == null ? 0 : $arr.length);
}
//字符串左取
String.prototype.left = function($num, $mode){
if(!/\d+/.test($num)) return(this);
$str = this.substr(0, $num);
if(!$mode) return $str;
$n = $str.tlength()-$str.length;
$num -= parseInt($n/2);
return this.substr(0, $num);
}
//字符串右取
String.prototype.right = function($num, $mode){
if(!/\d+/.test($num)) return(this);
$str = this.substr(this.length-$num);
if(!$mode) return $str;
$n = $str.tlength()-$str.length;
$num -= parseInt($n/2);
return this.substr(this.length-$num);
}
//字符串包含
String.prototype.get_count = function($str, $mode){
return eval('this.match(/(' + $str + ')/g' + ($mode ? 'i' : '') + ').length');
}
//字符串去除两端空字符
String.prototype.trim = function(){
return this.replace(/(^\s*)|(\s*$)/g,'');
}
String.prototype.ltrim = function(){
return this.replace(/(^\s*)/g, '');
}
String.prototype.rtrim = function(){
return this.replace(/(\s*$)/g, '');
}
function empty($obj) {
return !(String($obj) != '' && String($obj) != 'undefined');
}
function is_array($obj) {
return $obj.constructor == Array;
}
//判断字符串是否存在
function strexists($haystack, $needle) {
return $haystack.indexOf($needle) != -1;
}
//判断文件是否存在
function file_exists($name) {
if(FSO.FileExists(Server.MapPath($name))) {
return true;
} else {
return false;
}
return false;
}
//判断目录是否存在
function folder_exists($path) {
if(FSO.FolderExists(Server.MapPath($path))) {
return true;
} else {
return false;
}
return false;
}
//读取文件内容
function sreadfile($name) {
if(!file_exists($name)) {
return false;//文件不存在
} else {
$result = FSO.OpenTextFile(Server.MapPath($name)).ReadAll();
if($result == '') return false;//文件为空
return $result;
}
return false;
}
//写入文件
function swritefile($filename, $writetext) {
$op = FSO.OpenTextFile(Server.MapPath($filename), 2, true);
if($op.Write($writetext)) {
return true;
} else {
//这里可以是写入日志
return false;
}
}
//SQL ADDSLASHES
function saddslashes($string) {
}
//取消HTML代码
function shtmlspecialchars($string) {
$unallowed = {
'&': '&',
'"': '"',
'<': '<',
'>': '>'
};
for($p in $unallowed){
$string = $string.replace(eval('/'+$p+'/g'), $unallowed[$p]);
}
return $string;
}
//数据库连接
function dbconnect() {
}
//获取到表名
function tname($name) {
}
//对话框
function showmessage() {
}
//判断提交是否正确
function submitcheck($val) {
}
//检查是否登录
function checklogin() {
}
//模板调用
function template($name) {
if(strexists($name, '/')) {//自定义路径
$tpl = $name;
} else {//系统配置路径
$tpl = 'template/'+$_SCONFIG['template']+'/' + $name;
}
$objfile = './data/tpl_cache/'+$tpl.replace(/\//g,'_')+'.asp';
if(!file_exists($objfile)) {
parse_template($tpl);//解析输出到模板
}
return $objfile;
}
%>
最关键的是模板解析文件./source/function_templace.asp,可以这么说,网上没有找到一个很合适的模板解析文件,只有实现思路,代码都写得非常乱,没有一个很好的体系,请看我的代码:
<%
/**
* page build by Richmond 09-04-06
*/
$_SGLOBAL['i'] = 0;
$_SGLOBAL['block_search'] = new Array();
$_SGLOBAL['block_replace'] = new Array();
function parse_template($tpl) {
//包含模板
$_SGLOBAL['sub_tpls'] = new Array($tpl);
$tplfile = './'+$tpl+'.htm';
$objfile = './data/tpl_cache/'+$tpl.replace(/\//g,'_')+'.asp';
//read
if(!file_exists($tplfile)) {
$tplfile = $tplfile.replace('/'+$_SCONFIG['template']+'/', '/default/');
}
$template = sreadfile($tplfile);
if(empty($template)) {
Response.Write('Template file : '+tplfile+' Not found or have no access!');
Response.End();
}
//模板
$template = $template.replace(/\<\!\-\-\{template\s+([a-z0-9_\/]+)\}\-\-\>/ig, readtemplate);
//处理子页面中的代码
$template = $template.replace(/\<\!\-\-\{template\s+([a-z0-9_\/]+)\}\-\-\>/ig, readtemplate);
//ASP代码
$template = $template.replace(/\<\!\-\-\{eval\s+((?:.|\n)+?)\s*\}\-\-\>/ig, evaltags);
//开始处理
//变量
$template = $template.replace(/\<\!\-\-\{((?:.|\n)+?)\}\-\-\>/g, '\{$1\}');
$template = $template.replace(/([\n\r]+)\t+/g, '$1');
$template = $template.replace(/(\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\.([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/g, "$1['$2']");
$template = $template.replace(/\{(\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/g, '\<\%\=$1\%\>');
$template = $template.replace(/((\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)/g, addquote);
$template = $template.replace(/\<\%\=\<\%\=((\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(\[[a-zA-Z0-9_\-\.\"\'\[\]\$\x7f-\xff]+\])*)\%\>\%\>/g, addquote);
//逻辑
$template = $template.replace(/\{elseif\s+((?:.|\n)+?)\}/ig, stripvtags2);
$template = $template.replace(/\{else\}/ig, '\<\% \} else \{ \%\>');
//循环
$template = $template.replace(/\{loop\s+(\S+)\}((?:.|\n)+?)\{\/loop\}/ig, stripvtags4);
$template = $template.replace(/\{if\s+((?:.|\n)*?)\}((?:.|\n)+?)\{\/if\}/ig, stripvtags3);
//常量
$template = $template.replace(/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/g, '\<\%\=$1\%\>');
//替换
if(!empty($_SGLOBAL['block_search'])) {
for($i=1;$i<$_SGLOBAL['block_search'].length;$i++){
$template = $template.replace($_SGLOBAL['block_search'][$i], $_SGLOBAL['block_replace'][$i]);
}
}
//换行
$template = $template.replace(/ \?\>[\n\r]*\<\? /ig, ' ');
//write
if(!swritefile($objfile, $template)) {
//Response.Write('File: '+objfile+' can not be write!');
//Response.End();
}
}
function addquote() {
$var = '\<\%\='+arguments[1]+'\%\>';
return $var.replace(/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/g, "['$1']");
}
function evaltags() {
$_SGLOBAL['i']++;
$search = '\<\!\-\-EVAL_TAG_'+$_SGLOBAL['i']+'\-\-\>';
$_SGLOBAL['block_search'][$_SGLOBAL['i']] = $search;
$_SGLOBAL['block_replace'][$_SGLOBAL['i']] = '\<\% '+stripvtags(arguments[1])+' \%\>';
return $search;
}
function stripvtags($expr, $statement) {
if(typeof $statement == 'undefined') $statement = '';
$expr = $expr.replace(/\<\%\=(.+?)\%\>/g, '$1');
return $expr+$statement;
}
function stripvtags2() {
$expr = '\<\% \} else if('+arguments[1]+') \{ \%\>';
return $expr.replace(/\<\%\=(.+?)\%\>/g, '$1');
}
function stripvtags3() {
$expr = '\<\% if('+arguments[1]+') \{ \%\>'+arguments[2]+'\<\% \} \%\>';
return $expr.replace(/\<\%\=(.+?)\%\>/g, '$1');
}
function stripvtags4() {
$expr = '\<\% if(is_array('+arguments[1]+')) \{ for($i=0;i<$1.length;$i++) \{ \%\>'+arguments[2]+'\<\% \} \} \%\>';
return $expr.replace(/\<\%\=(.+?)\%\>/g, '$1');
}
//读取模板
function readtemplate() {
$name = arguments[1];
$tpl = strexists($name,'/')?$name:'template/'+$_SCONFIG['template']+'/'+$name;
$tplfile = './'+$tpl+'.htm';
if(!file_exists($tplfile)) {
$tplfile = $tplfile.replace('/'+$_SCONFIG['template']+'/', '/default/');
}
$content = sreadfile($tplfile);
return $content;
}
%>
现在举个简单的例子来证明:
首先在./template/default/目录下建立两个.htm模板文件,一个名为index.htm,一个名为sub.htm。
index.htm代码是这样的:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> New Document </TITLE>
<META NAME="Generator" C>
<META NAME="Author" C>
<META NAME="Keywords" C>
<META NAME="Description" C>
</HEAD>
<BODY>
<!--这是在模板中使用变量-->
<h1><!--{$_SCONFIG['sitename']}-->或者$_SCONFIG[sitename]</h1>
<!--这是在模板中使用asp代码-->
<!--{eval Response.Write("hello1");}-->
<!--{eval Response.Write("hello2");}-->
<!--{eval Response.Write("hello3");}-->
<!--这是在模板中使用逻辑语句-->
<!--{if $variable1}-->
输出变量1
<!--{elseif $variable2}-->
输出变量2
<!--{else}-->
输出默认变量
<!--{/if}-->
<!--这是在模板中使用循环语句-->
<!--{loop $my_array}-->
遍历数组成员
<!--{/loop}-->
<!--这是在模板中加载子模板-->
<!--{template sub}-->
</BODY>
</HTML>
注意看到上面<!--{template sub}-->了吗,它是调用子模板sub.htm,也就是说模板中再调用一次模板,这在使用包含公共文件时候非常有用的。
sub.htm的代码是这样的:
<!--{eval Response.Write("这个是sub.htm子模板");}-->
现在回到./index.asp页面中来,添加如下代码:
<!--#include file = 'common.asp'-->
<%
template('index');
%>
好,现在系统会在./data/tpl_cache目录下面生成一个缓存文件template_default_index.asp,这个文件名是有含义的,template表示模板,default表示模板的皮肤目录,index就是模板的源文件。可以打开./data/tpl_cache/template_default_index.asp缓存文件查看一下到底是些什么代码:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> New Document </TITLE>
<META NAME="Generator" C>
<META NAME="Author" C>
<META NAME="Keywords" C>
<META NAME="Description" C>
</HEAD>
<BODY>
<!--这是在模板中使用变量-->
<h1><%=$_SCONFIG['sitename']%>或者<%=$_SCONFIG['sitename']%></h1>
<!--这是在模板中使用asp代码-->
<% Response.Write("hello1"); %>
<% Response.Write("hello2"); %>
<% Response.Write("hello3"); %>
<!--这是在模板中使用逻辑语句-->
<% if($variable1) { %>
输出变量1
<% } else if($variable2) { %>
输出变量2
<% } else { %>
输出默认变量
<% } %>
<!--这是在模板中使用循环语句-->
<% if(is_array($my_array)) { for($i=0;i<$1.length;$i++) { %>
遍历数组成员
<% } } %>
<!--这是在模板中加载子模板-->
<% Response.Write("这个是sub.htm子模板"); %>
</BODY>
</HTML>
很好,已经成功把模板中的数据解析成ASP的代码了。
现在遗留下来最后一个问题也是最重要的问题,那就是,大家知道ASP中include file是没有办法动态包含文件的,而生成的asp缓存文件怎样才能动态加载进来并能够解析后台文件中的变量函数,这个问题一直困扰着我没有一个很好的解决方案,网上的答案众说纷纭,FSO、XMLHTTP+STREAM、或者APPLICATION等技术手段实现,希望有不错方案的朋友能够多多指教,跨越最后一道坎坷。
我现在的基本思路是使用FSO逐行读取来判断HTML和ASP代码分别作出选择执行Response.Write();或eval();,但是如果在一行同时出现HTML和ASP混合代码就比较麻烦了,不知道大家还有没有更好的实现思路...
以后的实现思路是这样的,系统在加载时会先读取缓存文件,如果缓存文件不存在就重新解析一下模板而后生成,从而避免每次都要解析生成模板,但前提是你的模板文件没有做过任何修改,如若不然,那只有删除缓存文件重新生成了,时间仓促,可能有些地方有疏漏或错误,但是这个思路和实现的模式能否给大家对ASP的未来带来一丝希望将拭目以待。