【问题标题】:Function local variables in cfccfc 中的函数局部变量
【发布时间】:2012-06-10 18:38:34
【问题描述】:

在我被要求研究 Coldfusion 应用程序中一些奇怪的间歇性错误之前,我没有使用过 Coldfusion。

在阅读了有关范围的信息后,我认为问题是因为我的 cfc 函数中的所有变量都没有使用 var 关键字,并且在各种函数中使用了相同的变量名。因此,据我了解,变量的范围是页面级别,调用这些函数的不同线程将覆盖导致“奇怪”问题的变量。

我的问题是这样做的正确方法是什么?

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

是否需要在每次设置 listCount 变量时添加var,还是仅在初始声明时添加?

【问题讨论】:

  • 您运行的是什么版本的 ColdFusion?
  • 这与你的问题无关,但实际上上面的代码中不需要循环。改用 cfqueryparam 既可以简化代码又有助于防止 sql 注入:即WHERE subjectid IN ( &lt;cfqueryparam value="#arguments.element_type_id#" cfsqltype="cf_sql_integer" list="true"&gt; )
  • 您使用的版本很重要。 ColdFusion 9 引入了一个新的 LOCAL 范围,专门用于函数。

标签: coldfusion scope coldfusion-9 cfc


【解决方案1】:

(我希望这个答案不会太啰嗦。我认为现有答案没有提供足够的信息,但希望没有走得太远……)


在 CF 中,有各种各样的作用域可以放置变量(应用程序、会话、url、cgi 等)。

其中一些需要使用显式声明(例如,会话变量必须始终具有范围),其他可以在读取变量时自动访问(例如,可以使用无范围变量读取表单和 url 变量) - 有一个这里的优先顺序决定了在哪些范围内检查无范围的变量。

此排序中的底部范围是variables 范围,这是适用于整个当前页面/对象实例的范围。

设置新变量时,如果没有作用域,则在variables 作用域中创建。由于这是一个全局范围,因此可以从同一函数的不同实例以及不同的函数访问它,这会导致您意识到的问题。


为了防止变量进入全局变量作用域,您必须将它放在函数的local 作用域中。 (从技术上讲,您可以将其放在函数的 arguments 范围内,但这可能会使人们感到困惑。)

在早期版本的 CF 中,无法显式访问本地范围 - 您需要使用 var 关键字才能在本地范围内创建变量 - 一旦创建,它将始终优先(两者用于读取和写入)在变量范围内。

在 CF9 中,local 范围现在是“正确”范围并且可以显式访问,因此您可以编写 &lt;cfset local.x = 0 /&gt; 而不是使用 &lt;cfset var x = 0 /&gt; - 这样做的主要好处是当您创建变量时不能使用 var 关键字的地方,例如&lt;cfquery name="local.qGetElementsByType" ...&gt;&lt;cfloop index="local.lcv"...&gt;

您仍然只需要在第一次创建每个变量时应用本地范围,以防止它进入变量范围 - 如果您愿意,后续读取/更新可以是无范围的,就像它们一样在执行 var 范围时。
(尽管无范围变量还有其他潜在的范围相关问题,例如在 &lt;cfloop query="queryname"&gt; 块内,因此有些人会争辩说您应该始终对所有变量进行范围无论如何。)


总而言之,要使您显示的代码安全,您需要范围:

  • qGetElementsByType 来自 cfquery 标签
  • lvc 来自 cfloop 标签

由于这些变量不是用 cfset 创建的,因此最容易通过在名称前加上 local. 前缀来确定范围

由于您已经 var 限定了 listCount 变量的范围,因此您无需在同一个函数中再次执行此操作 - 您可以选择使用 &lt;cfset local.listCount = local.listCount + 1&gt; (或者实际上是 &lt;cfset local.listCount++ &gt; )但这又是一个问题首选项,不需要防止泄漏到变量范围内。

(旁注:理想情况下,您应该使用 #lcv# 周围的 cfqueryparam 标记来防止 SQL 注入 - 即使这是一个查询查询,这可能仍然是一个问题,并且在安全性上始终保持安全总是更好.)

当然,这只是这个功能 - 您还需要修复其他功能 - 一个简单的方法是使用 varscoper 工具扫描您的整个代码库并识别需要作用域的变量。

【讨论】:

  • 谢谢,这很有帮助。来自 .net 的独家背景,这个coldfusion 范围界定让我有点摸不着头脑。所以这不是太啰嗦:)
  • Var scoper 似乎认为我的 local.varname 是一个 unvaredstruct。那样可以么?如果我首先执行 var local = {} 显然可以解决问题,但这只是 varscoper 不知道我使用的是高于 9 的 CF 版本吗?
  • 是的,如果您使用的是 CF9 或更高版本就可以了。如果您使用的是 CF8,则需要 var local = {}。我想 varscoper 中应该有一个选项来考虑代码的版本。哦,我认为 varscoper 也可能存在一些与脚本相关的问题,这可能是造成这种情况的原因。
【解决方案2】:

除了 Evik 在他的回答中所说的之外,你应该 VAR all 你的函数局部变量。 qGetElementsByTypelcv 也应该是 VARed。如果您使用的是 CF9 及更高版本,您可以简单地使用 LOCAL 范围来确定它们的范围,例如:local.qGetElementsByType 等。

【讨论】:

    【解决方案3】:

    我假设您的问题是专门针对这一行的:

    <cfset listCount = listCount + 1>
    

    不,您不需要在此行中再次使用 var。

    但是,如果稍后在页面中,您尝试使用访问名为 listCount 的变量,它已经具有您刚刚运行的代码中的值,因此您需要重新创建它。

    【讨论】:

    • 我认为 var 的意义在于它使它成为函数的本地变量,所以如果我试图访问一个名为“listCount”的变量,它就不会存在?
    • 第一次创建变量时需要var。然后它将绑定到函数的上下文(是的,不会存在于函数之外)。
    • @Omiron,您没有在函数中显示您的代码。你提到了一个功能,但你没有展示你的功能。这就是为什么我不能完全回答你的问题。
    • 好吧对不起,应该提到我粘贴的代码是一个函数的内容
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多