views:

750

answers:

5

Does ASP Classic have a feature that is the equivalent of PHP's "include once" feature?

+4  A: 

No. Sorry. You have to manage includes yourself.

Rashack
+2  A: 

The best you can do is using SSI to include files:

http://en.wikipedia.org/wiki/Server_Side_Includes

FlySwat
+1  A: 

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.

+1  A: 

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:

  • This uses a faux function simulated with a default property. If this bothers you, it's easily refactored.
  • 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
    
Thom Smith
+1 Excellent answer. It's a shame I didn't have it back then.
C. Ross
A: 

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
Antony Booth