将ASP原生代码编译生成静态类
老早就说放弃ASP了,但这段时间以来因为工作需要,始终放不下。这不,最近又写了个东西出来,分享一下。
这个标题有点纠结,实在不知道该起个什么名字。功能上来讲,它是将原生的ASP代码编译后返回ASP执行的结果,这个结果就是打开ASP页面看到的html代码,写这个功能是忽然想到可以借这个方法生成静态,不必创建一个XMLHttp请求对原生的ASP实施静态生成
将这些文件解压到一个文件夹内
可以在浏览器访问到的
1.访问Test.asp,这个结果为直接运行ASP文件的结果
2.访问CompileTest.asp 这个结果为编译ASP文件后输出的结果,并且可以生成静态文件
用途:
1.用于ASP编写的页面生成静态
相对于传统方法生成静态文件需要用到XMLHttp组件,从外部网络访问获取到内容,再创建文件
最重要的是减少了网络延迟,解决了部分服务器不支持组件,或者不支持自身的WEB访问
对服务器压力是否有减少并没有做过测试,理论上说这种方式只是一个会话就可以生成文件,传统的方式要从web访问,至少增加了一倍的会话量
2.用于ASP模板套用及解析
ASP在这方面一直是弱项,也有不少成熟的模板引擎,但有一大部分是编译型,而不是解释型,编译型模板引擎是将ASP模板代码编译成原生的ASP代码,再执行。我之前也发布过一个解释型模板引擎,自己还是比较满意的,但不方便的是使用的人,需要另外学习模板语法,虽然简单,对于不熟悉程序的设计师来说,不如懂一点ASP就好的
必要文件:
xCompile.Class.asp
xDictionary.Class.asp
其它均为测试用文件
需要注意的问题:
1.设定好预包含文件和忽略文件
预包含文件为必须的功能性文件,不包含任何html代码输出,一般是类集或函数集
忽略的文件一般是当前的文件已经包含过的文件
2.参数问题
只考虑了Request.QueryString参数,并且只支持编译文档内的参数
不支持函数中的参数
3.其它问题欢迎反馈至 shirne@126.com
测试代码:
<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<!--#Include file="xDictionary.Class.asp"-->
<!--#Include file="xCompile.Class.asp"-->
<%
Dim C
Set C=New xCompile
'//这个文件会被预先运行,文件路径写法不重要,比较时是按实际磁盘路径比较的
C.AddInclude "TestFunctions.asp"
'//ASP中调用到这个函数的地方会被替换成字符串连接
C.AddOutFun "Echo"
C.Param("param")="这是传过去的参数"
'//编译文件
C.Compile "Test.asp"
C.Clear
'//保存编译后的文件到内容
C.SaveTo "index.html"
'//输出编译后的内容
Response.Write C
%>
原代码如下(老规矩,里面的一些关键字因服务器禁止,替换了个中文字符):
'+++++++++++++++++++++++++++++++++++
'ASP编译类
'可按设定直接将ASP文件编译运行返回结果
'在原生ASP中用来生成静态文件,而不采用获取Http页面的方法
'shirne@126.com
'http://www.shirne.com
'+++++++++++++++++++++++++++++++++++
Class xCompile
Private Includes '预包含文件
Private Ignores '忽略的包含文件
Private OutFun '输出函数
Private strHtml '最终的HTML字符串
Private Params '参数,仅支持QueryString参数,生成静态不建议使用其它参数
Private FuncStr '定义的全局函数名
Private Classes '已加载的类列表,ASP中类不能重复加载
Private Prepared
Private Sub Class_Initialize
OutFun = Array("Response.Write")
FuncStr = "Outer__Html__Str"
Set Classes=Server.CreateObject("Scripting.Dictionary")
Classes.CompareMode=1
Set Params=new xDictionary
Params.Init Request.QueryString
Prepared = -1
End Sub
Private Sub Class_Terminate
Classes.RemoveAll
Set Classes= Nothing
Set Params = Nothing
End Sub
Public Default Property Get Html
Html = strHtml
End Property
'主要动作是处理预包含文件
Public Property Get Prepare
If IsArray(Includes) Then
Dim i,L:L=UBound(Includes)
If Prepared>=L Then Exit Property
For i=Prepared+1 To L
Require Includes(i)
Next
Prepared = L
End If
End Property
'添加/获取参数
Public Property Let Param(key, val)
Params.Replace key,val
End Property
Public Property Get Param(key)
Param = Params(key)
End Property
'添加预包含文件
Public Sub AddInclude( File)
Includes = Merge(Includes,CheckTruePath(File))
End Sub
'添加忽略文件
Public Sub AddIgnore( File)
Ignores = Merge(Ignores,CheckTruePath(File))
End Sub
'添加输出函数
Public Sub AddOutFun( Fun)
OutFun = Merge(OutFun,Fun)
End Sub
'保存
Public Sub SaveTo( path)
WriteFile path, strHtml
End Sub
'检查是否磁盘路径,返回磁盘路径,可接收数组
Private Function CheckTruePath( obj)
If IsArray(obj) Then
Dim i
For i=0 To UBound(obj)
If InStr(obj(i),":")<1 Then
obj(i) = Server.MapPath(obj(i))
End If
Next
CheckTruePath = obj
Else
obj = obj&""
If InStr(obj,":")>0 Then
CheckTruePath = obj
Else
If obj<>"" Then CheckTruePath = Server.MapPath(obj)
End If
End If
End Function
'编译文件
Public Sub Compile(File)
'读取文件内容
strHtml = ReadFile(File)
If strHtml="" Then
Exit Sub
End If
'包含文件
strHtml = Include(strHtml,File)
'去除页面指令
strHtml = RegReplace("<%@[^>]+%\>",strHtml,"")
Dim arrHtml, i, j, k, l
i = InStr(strHtml,"<%")
If i>0 Then
'存在ASP标签,则逐个解析
ReDim arrHtml(0)
j = 0
k = 1
Do Until i<1
l = InStr(i+1,strHtml,"%\>")
If l<1 Then Err.Raise 7,"ASP语法错误,不正确的闭合标签"
ReDim Preserve arrHtml(j+1)
arrHtml(j) = FuncStr &" = "& FuncStr &" &"""&EscapeHtml(Mid(strHtml,k,i-k))&""""
arrHtml(j+1)=Mid(strHtml,i+2,l-i-2)
If Left(arrHtml(j+1),1)="=" Then
arrHtml(j+1) = FuncStr &" = "& FuncStr &" &"& EscapeAsp(Mid(arrHtml(j+1),2))
Else
arrHtml(j+1) = EscapeAsp(arrHtml(j+1))
End If
j = j + 2
k = l + 2
i = InStr(l+1,strHtml,"<%")
Loop
'最后的html内容
ReDim Preserve arrHtml(j)
arrHtml(j) = FuncStr &" = "& FuncStr &" &"""&EscapeHtml(Mid(strHtml,l+2))&""""
strHtml = Join(arrHtml,vbCrLf)
'过滤类
strHtml = FilterClass(strHtml)
'过滤Sub
strHtml = FilterSub(strHtml)
'过滤Function
strHtml = FilterFunction(strHtml)
'执行预包含文件
PrePare
'Response.Write "Function "& FuncStr &"(Param)" & vbCrLf & strHtml & vbCrLf &"End Function"
ExecuteGlobal "Function "& FuncStr &"(Param)" & vbCrLf & strHtml & vbCrLf &"End Function"
strHtml = Eval(FuncStr&"(Params)")
End If
End Sub
'清理处理后的内容
Sub Clear
Dim i
strHtml = xTrim(strHtml,Chr(32)&Chr(9)&Chr(10)&Chr(13))
'清理多余的空格
i = InStr(strHtml,Chr(32) & Chr(32))
While i>0
strHtml = Replace(strHtml,Chr(32) & Chr(32),Chr(32))
i = InStr(strHtml,Chr(32) & Chr(32))
Wend
'清理多余的空行
i = InStr(strHtml,vbCrLf & vbCrLf)
While i>0
strHtml = Replace(strHtml,vbCrLf & vbCrLf,vbCrLf)
i = InStr(strHtml,vbCrLf & vbCrLf)
Wend
End Sub
Private Function Require(File)
Dim html, absPath
html = ReadFile(File)
absPath = Mid(File,Len(Server.MapPath("/")))
html = Include(html,absPath)
Dim arrHtml, i, j, k, l
i = InStr(html,"<%")
If i>0 Then
'存在ASP标签,则逐个解析,否则忽略该文件
ReDim arrHtml(0)
j = 0
k = 1
Do Until i<1
l = InStr(i+1,html,"%\>")
If l<1 Then Err.Raise 7,"ASP语法错误,不正确的闭合标签"
ReDim Preserve arrHtml(j+1)
arrHtml(j) = "" '忽略所有非asp内容
arrHtml(j+1)=Mid(html,i+2,l-i-2)
j = j + 2
k = l + 2
i = InStr(l+1,html,"<%")
Loop
ExecuteGlobal Join(arrHtml,vbCrLf)
End If
End Function
Private Function EscapeHtml( html)
If InStr(html,"""")>0 Then html = Replace(html,"""","""""")
If InStr(html,vbCrLf)>0 Then html = Replace(html,vbCrLf,"""& vbCrLf &""")
If InStr(html,Chr(10))>0 Then html = Replace(html,Chr(10),"")
If InStr(html,Chr(13))>0 Then html = Replace(html,Chr(13),"")
'If InStr(html,"&""""&")>0 Then html = Replace(html,"&""""&","&")
EscapeHtml = Replace(html,Chr(0),"")
End Function
Private Function EscapeAsp( html)
Dim i
For i=0 To UBound(OutFun)
If InStr(1,html,OutFun(i),1)>0 Then
html = RegReplace("\b"& OutFun(i) &"\b",html,FuncStr &" = "& FuncStr &" &")
End If
Next
If InStr(1,html,"Request.QueryString(",1)>0 Then
html = RegReplace("\bRequest\.QueryString\(",html,"Param(")
End If
EscapeAsp = html
End Function
'包含入文件
Private Function Include(html,ByVal path)
Dim Matches,Match,iHtml, iPath, oHtml, lastIndex
Set Matches=REObject("<!--\s*#include\s+(file|virtual)=""([^*?<>=:""|]+)""\s*-->","gi").Execute(html)
If Matches.Count>0 Then
lastIndex = 1
For Each Match In Matches
oHtml = oHtml & Mid(html,lastIndex,Match.FirstIndex+1-lastIndex)
If StrComp(Match.SubMatches(0),"file",1)=0 Then
iPath = getDir(path) & Match.SubMatches(1)
ElseIf StrComp(Match.SubMatches(0),"virtual",1)=0 Then
iPath = Match.SubMatches(1)
Else
iPath = ""
End If
If CheckNeedInclude(iPath) Then
iHtml = ReadFile(iPath)
iHtml = Include(iHtml,iPath)
oHtml = oHtml & iHtml
End If
lastIndex = Match.FirstIndex+Match.Length+1
Next
oHtml = oHtml & Mid(html, lastIndex)
Include = oHtml
Else
Include = html
End If
End Function
'检查是否需要包含
Private Function CheckNeedInclude(ByVal path)
CheckNeedInclude = True
If path="" Then CheckNeedInclude = False:Exit Function
path = CheckTruePath(path)
Dim i
'先检查预包含文件
If IsArray(Includes) Then
For i=0 To UBound(Includes)
If StrComp(Includes(i),path,1)=0 Then
CheckNeedInclude = False
Exit Function
End If
Next
End If
'再检查忽略含文件
If IsArray(Ignores) Then
For i=0 To UBound(Ignores)
If StrComp(Ignores(i),path,1)=0 Then
CheckNeedInclude = False
Exit Function
End If
Next
End If
End Function
Private Function FilterClass( html)
Dim Matches,Match,ClassName,oHtml,LastIndex
Set Matches=REObject("\bClass\s+([\w\d\_]+)\b[\s\S]+?\bEnd\s+Class\b","ig").Execute(html)
If Matches.Count>0 Then
LastIndex = 1
For Each Match In Matches
oHtml = oHtml & Mid(html,LastIndex,Match.FirstIndex+1-LastIndex)
ClassName = Match.SubMatches(0)
If Classes.Exists(ClassName)=False Then
'直接执行类代码,没有处理类内部的属性,函数等功能的输出,尽量事先处理好
ExecuteGlobal Match.Value
Classes.Add ClassName,1
End If
LastIndex = Match.FirstIndex+Match.Length+1
Next
oHtml = oHtml & Mid(html, lastIndex)
FilterClass = oHtml
Else
FilterClass = html
End If
End Function
Private Function FilterSub( html)
Dim Matches,Match,SubName,SubStr,oHtml,LastIndex
Set Matches=REObject("\bSub\s+([\w\d\_]+)\b[\s\S]+?\bEnd\s+Sub\b","ig").Execute(html)
If Matches.Count>0 Then
LastIndex = 1
For Each Match In Matches
oHtml = oHtml & Mid(html,LastIndex,Match.FirstIndex+1-LastIndex)
SubName = Match.SubMatches(0)
SubStr = Match.Value
'这里替换没有排除字符串内部的关键字
SubStr = RegReplace("\bSub\b",SubStr,"Function") '将Sub替换成Function
ExecuteGlobal RegReplace("\b"& FuncStr &"\b",SubStr,SubName)
LastIndex = Match.FirstIndex+Match.Length+1
Next
oHtml = oHtml & Mid(html, lastIndex)
FilterSub = oHtml
Else
FilterSub = html
End If
End Function
Private Function FilterFunction( html)
Dim Matches,Match,FunctionName,FunctionStr,oHtml,LastIndex
Set Matches=REObject("\bFunction\s+([\w\d\_]+)\b[\s\S]+?\bEnd\s+Function\b","ig").Execute(html)
If Matches.Count>0 Then
LastIndex = 1
For Each Match In Matches
oHtml = oHtml & Mid(html,LastIndex,Match.FirstIndex+1-LastIndex)
FunctionName = Match.SubMatches(0)
FunctionStr = Match.Value
'这里替换没有排除字符串内部的关键字,函数名起特殊一点一般可以避免
ExecuteGlobal RegReplace("\b"& FuncStr &"\b",FunctionStr,FunctionName)
LastIndex = Match.FirstIndex+Match.Length+1
Next
oHtml = oHtml & Mid(html, lastIndex)
FilterFunction = oHtml
Else
FilterFunction = html
End If
End Function
End Class
这里是我用到的版本的原代码,下面这个压缩包里与这个代码有点差别,就是将类中用到的几个常用函数也封装进来了,方便大家测试,另外还有一个里面用到的xDictionary类
注:改善后的新版地址:解析原生ASP代码的模板引擎(完善版)