Reputation: 31848
Does ASP Classic have a feature that is the equivalent of PHP's "include once" feature?
Upvotes: 16
Views: 6730
Reputation: 198
This is how you include a file into Asp Classic.
include this it would look gray out, but it will include the file.
Upvotes: 0
Reputation: 6384
Thanks Thom Smith for your piece of code.
Below a version based on yours to handle UTF8 source files (and who manages opening & closing ASP tags).
Dim IncludeOnce
Class IncludeOnceClass
Private libraries_
Private adostream_
Private Sub CLASS_INITIALIZE
Set libraries_ = Server.createObject("Scripting.Dictionary")
Set adostream_ = CreateObject("ADODB.Stream")
adostream_.CharSet = "utf-8"
adostream_.Open
End Sub
Public Default Property Get includeFile (path)
Dim lpath : lpath=LCase(path)
if Not libraries_.exists(lpath) then
Dim code
libraries_.add LCase(lpath), "stuff"
On Error Resume Next
adostream_.LoadFromFile(lpath)
code = Replace(Replace(adostream_.ReadText(),"<"&"%",""),"%"&">","")
executeGlobal code
If Err.number <> 0 Then
Response.write "Error importing library "
Response.write lpath & "<br>"
Response.write Err.source & ": " & Err.description
Response.write "<pre><tt>" & replace(code,"<","<") & "</tt></pre>"
Response.End
End if
On Error Goto 0
End If
End Property
End Class
Set IncludeOnce = new IncludeOnceClass
As you see, I don't close the stream to let further includes be processed.
Update: Since paths are case insensitive, this version converts them to lower case before adding to the Dictionary as keys. Also they are added before code evaluation, to prevents duplicate evaluations in case of inclusion loop (recursion) :
Given A.asp and B.asp :
' A.asp
IncludeOnce Server.MapPath("/some/path/B.asp")
' B.asp
IncludeOnce Server.MapPath("/some/path/A.asp")
Since A
is not yet evaluated at all at B
code inclusion, the second IncludeOnce
will occur a duplicate evaluation if it's not yet registered in the dictionary, which we won't want. This update fixes the issue.
Upvotes: 3
Reputation: 248
As a caveat to the Answer using ExecuteGlobal ; I think that ExecuteGlobal creates instability as VBScript does not garbage collect or manage memory well. It may throw "Catastrophic failure" or "ASP 0240 Script Engine Exception" errors because you are redefining an object that was not correctly disposed of.
So, to use this, you would also need to make your includes, Classes that have destructors and the include should create an instance of the class, persist the object name in the dictionary and the ImportFunction destructor should destroy all objects in the dictionary.
Includes should not be prefixed with ASP tags <% %> either, whereas using an #include, you would need to.
You should also note that ExecuteGlobal, considers all X = Y to be assignment and not comparison, so: -
isMatch = (X = Y) ' may produce spurious results.
Example include: -
Class aClass
Public Sub doSomething()
...
End Sub
End Class
Dim instance
Set instance = new aClass
I have created an example similar to the one above, but using the second style of passing the include to a Sub or property assignment
What it also does, is attempts to garbage collect by destroying all objects in the dictionary, in it's own destructor. It also strips the asp tags for libraries you wrapped.
<%
Class Libraries
private m_LoadedLibraries
private m_FileSystem
Private Property Get LoadedLibraries
If Not isInstantiated( m_LoadedLibraries ) Then
Set m_LoadedLibraries = Server.CreateObject("Scripting.Dictionary")
End If
Set LoadedLibraries = m_LoadedLibraries
End Property
Private Property Get FileSystem
If Not isInstantiated( m_FileSystem ) Then
Set m_FileSystem = Server.CreateObject("Scripting.FileSystemObject")
End If
Set FileSystem = m_FileSystem
End Property
Private Property Get IncludePath
IncludePath = "c:\include\"
End Property
Private Property Get LibrarySuffix
LibrarySuffix = ".inc.asp"
End Property
Private Sub Class_Terminate()
destroyObjects
Set m_LoadedLibraries = Nothing
Set m_FileSystem = Nothing
End Sub
Private Sub destroyObjects()
Dim objectName
For Each objectName in LoadedLibraries.Items
If Eval("IsObject(" & objectName & ")") Then
Execute "Set " & objectName & " = Nothing"
End If
Next
End Sub
Public Sub Include(ByVal libraryName, ByVal objectName)
libraryName = LCase( libraryName )
Dim library, filePath, script
If Not LoadedLibraries.Exists( libraryName ) Then
filePath = IncludePath + libraryName + LibrarySuffix
Set library = FileSystem.OpenTextFile( filePath )
script = library.ReadAll
script = stripAspTags( script )
ExecuteGlobal script
library.Close
LoadedLibraries.add libraryName, objectName
End If
End Sub
Private Function stripAspTags(ByVal script)
Dim buffer, location, startTag, stopTag, tagLength
startTag = Chr(60) + "%"
stopTag = "%" + Chr(62)
script = CStr( script )
tagLength = Len(startTag)
location = InStr(1, script, startTag, 1 )
If location > 0 Then
buffer = cleanString( Left( script, location + tagLength - 1 ) )
' Only strip off if it's the first part of the script
If Len( buffer ) = Len(startTag) Then
script = Mid( script, location + tagLength )
End If
End If
location = InStrRev(script, stopTag, -1, 1 )
If location > 0 Then
buffer = cleanString( Mid( script, location ) )
' Only strip off if it's the last part of the script
If Len( buffer ) = Len(stopTag) Then
script = Left( script, location - 1 )
End If
End If
stripAspTags = script
End Function
Private Function cleanString(ByVal target)
Dim objRegExp
Set objRegExp = New Regexp
objRegExp.IgnoreCase = True
objRegExp.Global = True
objRegExp.Pattern = "[\x00-\x1f]+"
cleanString = objRegExp.Replace( target, "")
Set objRegExp = Nothing
End Function
Private Function isInstantiated(ByVal target)
isInstantiated = ( IsNull( target) Or IsEmpty( target ) Or Not IsObject( target) )
End Function
End Class
Dim RequireOnce
Set RequireOnce = new Libraries
%>
Usage: -
With RequireOnce
.Include "aClass", "instance"
.Include "Database", "DataAccess"
.Include "Constants", "Constant"
.Include "LoginCheck", "Login"
End With
Upvotes: 1
Reputation: 14086
I know this is an old topic, but I thought I'd add my two cents in case anyone's interested.
I wrote a function that does precisely what you want: includes the given file exactly one no matter how many times it was called.
class ImportFunction
private libraries_
private fso_
private sub CLASS_INITIALIZE
set libraries_ = Server.createObject("Scripting.Dictionary")
set fso_ = Server.createObject("Scripting.FileSystemObject")
end sub
public default property get func (path)
if not libraries_.exists(path) then
on error resume next
with fso_.openTextFile(path)
executeGlobal .readAll
if Err.number <> 0 then
Response.write "Error importing library "
Response.write path & "<br>"
Response.write Err.source & ": " & Err.description
end if
end with
on error goto 0
libraries_.add path, null
end if
end property
end class
dim import: set import = new ImportFunction
Notes:
The dictionary must be persistent through all includes, whereas persisting the fso merely avoids reconstructing it. If you don't like the idea of keeping them around after the imports are done, you could modify the class to get syntax like this:
with new Importer
.import "foo"
.import "bar/baz"
end with
Upvotes: 16
Reputation:
This was one of the issue's I first ran into after inheriting a legacy ASP app. My solution currently is include all the genric library files in a file called 'app-init.asp'. This file is then included in all of pages and give's you a single point of entry to hook up app wide functionality and a base to build onto.
Upvotes: 2
Reputation: 175593
The best you can do is using SSI to include files:
http://en.wikipedia.org/wiki/Server_Side_Includes
Upvotes: 1