<cfcomponent output="false">
<cffunction name="displayForm" access="public" output="false" returntype="string">
<cfargument name="formID" required="true" type="numeric" />
<cfset var local = StructNew() />
<!--- get the templates associated with the form --->
<cfquery name="local.getTemplates" datasource="#application.datasource#">
select file_path
from form_templates
where form_id = <cfqueryparam value="#arguments.formID#" cfsqltype="cf_sql_integer" />
order by sort_order asc
</cfquery>
<!--- capture the generated html --->
<cfsavecontent variable="local.html">
<!--- loop over the templates and display (include) each template --->
<cfoutput query="local.getTemplates">
<cfinclude template="#local.getTemplates.file_path#" />
</cfoutput>
</cfsavecontent>
<cfreturn local.html />
</cffunction>
</cfcomponent>
Hopefully that's straightforward enough. This works just fine at first, until you store the component in the application scope as a singleton and a developer doesn't properly scope a variable inside one of the included templates, thereby creating a public variable that could bleed into other requests.
The solution is pretty simple: rather than using the cfinclude tag to include the mixin, use a helper function that mimics the functionality but traps the variables.
Doing this requires creating an include helper and an include proxy. The helper is created as a singleton and its purpose is to create instances of the proxy, which is in charge of including the original file.
<cfcomponent displayname="Include Helper" output="false">
<cffunction name="template" access="public" output="true" returntype="any">
<cfargument name="template" required="true" type="string" />
<cfreturn createObject("component","includeProxy").init(arguments.template) />
</cffunction>
</cfcomponent>
<cfcomponent displayname="Include Proxy" output="false">
<cffunction name="init" access="public" output="true" returntype="void">
<cfargument name="template" required="true" type="string" />
<cfinclude template="#arguments.template#" />
</cffunction>
</cfcomponent>
With the include components creating, I can now replace <cfinclude template="#local.getTemplates.file_path#" /> with #helpers.include.template(local.getTemplates.file_path)# and the template variables are now properly hidden from my component.
An alternative, although slightly hackier, way of doing it is to temporarily "redirect" the component variables scope: http://gist.github.com/95715
ReplyDeleteBy var-ing a new 'variables' scope it effectively localises any variable usage (scoped or unscoped) within both the function call and any included files.