Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function local variables in cfc

Having not worked with coldfusion before I was asked to look into some strange intermittent bugs in a coldfusion application.

After reading about scopes I believe the problem is because none of the variables in my cfc functions are using the var keyword, and the same variable name is used in various functions. So as I understand it the variables are scoped at page level and different threads calling these functions will be overwriting the variable causing the "strange" problems.

My question is what is the proper way to do this?

 <cfset var listCount = 0>
 <cfquery name="qGetElementsByType" dbtype="query" maxrows="#arguments.num_to_return#">
    SELECT elementId,
           title, PIhtml, Rerhtml,
           text, url, image, Rank, isPoll, pollId, subjectId
    FROM   arguments.element_query
    WHERE  <cfloop list="#arguments.element_type_id#" index="lcv">
               <cfif listCount GT 0>
                  OR
               </cfif>
               subjectid =  #lcv#
              <cfset listCount = listCount + 1>
           </cfloop>
</cfquery>

Does var need to be added each time the listCount variable is set, or just on the initial declaration?

like image 981
Omiron Avatar asked Dec 15 '22 23:12

Omiron


1 Answers

(I hope this answer isn't too long-winded. I didn't think the existing answers gave sufficient information, but hopefully haven't gone too far the other way...)


In CF, there are assorted scopes which variables can be placed in (application,session,url,cgi,etc).

Some of these require explicit declaration to be used (e.g. session variables must always be scoped), others can be automatically accessed when reading a variable (e.g. form and url variables can be read by using an unscoped variable) - there is an order of precedence here which determines which scopes are checked for an unscoped variable.

The bottom scope in this ordering is the variables scope, which is a scope which applies for the entire current page/object instance.

When setting a new variable, if unscoped, it is created in the variables scope. Since this is a global scope, it can be accessed from both different instances of the same function, as well as difference functions, which causes the issues of which you're aware.


To prevent a variable going into the global variables scope you must place it in the function's local scope. (Technically you can place it in the function's arguments scope, but this will likely confuse people.)

In earlier versions of CF, there was no way to explicitly access the local scope - you needed to use the var keyword in order to create the variable inside the local scope - and once created it will always take precedence (both for reading and writing) over the variables scope.

With CF9, the local scope is now a "proper" scope and can be accessed explicitly, so instead of using <cfset var x = 0 /> you can write <cfset local.x = 0 /> - the main benefit of this is when you are creating a variable where the var keyword cannot be used, e.g. <cfquery name="local.qGetElementsByType" ...> and <cfloop index="local.lcv"...>

You still only need to apply the local scope when first creating each variable, to prevent it going into the variables scope - subsequent reads/updates can be unscoped if you prefer, just as they would be when doing the var scope.
(Although there are other potential scope-related issues with unscoped variables, such as inside of a <cfloop query="queryname"> block, and because of this some people will argue you should always scope all variables no matter what.)


In summary, to make the code you show safe, you need to scope:

  • qGetElementsByType from the cfquery tag
  • lvc from the cfloop tag

Since these variables are not created with cfset, scoping is most easily done by prefixing the names with local.

Since you have already var scoped the listCount variable, you don't need to do it again in the same function - you can optionally use <cfset local.listCount = local.listCount + 1> (or indeed <cfset local.listCount++ > ) but again this is a matter of preference and not needed to protect against leaking into the variables scope.

(side note: Ideally, you should use the cfqueryparam tag around #lcv# to protect against SQL injection - even though this is a query of query this might still be an issue, and it's always better to play it safe on security.)

Of course, that's just this function - you'll also need to fix other functions - and an easy way to do that is to use the varscoper tool to scan your entire codebase and identify variables that need scoping.

like image 143
Peter Boughton Avatar answered Jan 04 '23 20:01

Peter Boughton