Функциональные локальные переменные в cfc

Я не работал с 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>

Нужно ли добавлять var каждый раз, когда устанавливается переменная listCount, или только при первоначальном объявлении?


person Omiron    schedule 10.06.2012    source источник
comment
Какую версию ColdFusion вы используете?   -  person Evik James    schedule 10.06.2012
comment
Это не имеет ничего общего с вашим вопросом, но на самом деле в приведенном выше коде нет необходимости в циклах. Вместо этого использование cfqueryparam упростит код и поможет защититься от SQL-инъекций: т.е. WHERE subjectid IN ( <cfqueryparam value="#arguments.element_type_id#" cfsqltype="cf_sql_integer" list="true"> )   -  person Leigh    schedule 11.06.2012
comment
Версия, которую вы используете, важна. В ColdFusion 9 появилась новая область LOCAL специально для использования в функциях.   -  person Evik James    schedule 11.06.2012


Ответы (3)


(Надеюсь, этот ответ не слишком многословен. Я не думаю, что существующие ответы дали достаточно информации, но, надеюсь, не зашли слишком далеко...)


В CF есть разные области, в которые могут быть помещены переменные (приложение, сеанс, URL, cgi и т. д.).

Некоторые из них требуют использования явного объявления (например, переменные сеанса всегда должны иметь область действия), другие могут быть доступны автоматически при чтении переменной (например, переменные формы и URL-адреса могут быть прочитаны с использованием переменной без области видимости) - существует порядок приоритета. здесь, который определяет, какие области проверяются для переменной без области.

Нижняя область в этом порядке — это область variables, которая применяется ко всему текущему экземпляру страницы/объекта.

При установке новой переменной, если она не определена, она создается в области variables. Поскольку это глобальная область, к ней можно получить доступ как из разных экземпляров одной и той же функции, так и из разных функций, что вызывает проблемы, о которых вы знаете.


Чтобы переменная не попала в область глобальных переменных, вы должны поместить ее в область видимости local функции. (Технически вы можете поместить его в область действия функции arguments, но это, скорее всего, запутает людей.)

В более ранних версиях CF не было возможности явного доступа к локальной области — вам нужно было использовать ключевое слово var, чтобы создать переменную внутри локальной области — и после создания она всегда будет иметь приоритет (как для чтения, так и для записи). над областью переменных.

В CF9 область local теперь является "правильной" областью действия и к ней можно получить доступ явно, поэтому вместо использования <cfset var x = 0 /> вы можете написать <cfset local.x = 0 /> - основное преимущество этого заключается в том, что вы создаете переменную, в которой нельзя использовать ключевое слово var, например. <cfquery name="local.qGetElementsByType" ...> и <cfloop index="local.lcv"...>

Вам по-прежнему нужно применять локальную область видимости только при первом создании каждой переменной, чтобы она не попала в область переменных — последующие операции чтения/обновления могут быть неограничены, если вы предпочитаете, точно так же, как при выполнении var scope.
(Хотя есть и другие потенциальные проблемы, связанные с областью действия переменных без области видимости, например, внутри блока <cfloop query="queryname">, и из-за этого некоторые люди будут утверждать, что вы всегда должны охватывать все переменные, несмотря ни на что.)


Подводя итог, чтобы сделать код, который вы показываете, безопасным, вам необходимо определить область действия:

  • qGetElementsByType из тега cfquery
  • lvc из тега cfloop

Поскольку эти переменные не создаются с помощью cfset, определение области действия проще всего выполнить, добавив к именам префикс local..

Поскольку вы уже определили область видимости переменной listCount в var, вам не нужно делать это снова в той же функции — вы можете дополнительно использовать <cfset local.listCount = local.listCount + 1> (или даже <cfset local.listCount++ > ), но опять же это вопрос предпочтений и не требуется для защиты от утечки в область действия переменных.

(примечание: в идеале вы должны использовать тег cfqueryparam вокруг #lcv# для защиты от SQL-инъекций - даже если это запрос запроса, это все еще может быть проблемой, и всегда лучше перестраховаться в плане безопасности.)

Конечно, это только эта функция — вам также нужно исправить другие функции — и простой способ сделать это — использовать varscoper инструмент для сканирования всей вашей кодовой базы и определения переменных, которые нуждаются в области видимости.

person Peter Boughton    schedule 10.06.2012
comment
Спасибо, это полезно. Исходя из исключительно .net-фона, этот обзор coldfusion занимает у меня немного времени. Так что это не слишком долго :) - person Omiron; 11.06.2012
comment
Var scoper, похоже, считает, что мой local.varname является неизменяемой структурой. Это нормально? Если я сначала выполню var local = {}, очевидно, это решит проблему, но это просто varscoper не знает, что я использую версию CF выше 9? - person Leeish; 15.01.2013
comment
Да, если у вас CF9 или выше, все в порядке. Если вы используете CF8, вам нужно var local = {}. Я предполагаю, что в varscoper должна быть возможность учитывать, для какой версии предназначен код. О, я думаю, что могут быть некоторые проблемы, связанные со сценариями, с varscoper, которые могут быть причиной этого. - person Peter Boughton; 15.01.2013

В дополнение к тому, что Эвик говорит в своем ответе, вы должны VAR все локальные переменные вашей функции. qGetElementsByType и lcv тоже должны быть VARed. Если вы используете CF9 и далее, вы можете просто использовать область видимости LOCAL, например: local.qGetElementsByType и т. д.

person Adam Cameron    schedule 10.06.2012

Я предполагаю, что ваш вопрос конкретно касается этой строки:

<cfset listCount = listCount + 1>

Нет, вам не нужно снова использовать var в этой строке.

Однако, если позже на странице вы попытаетесь использовать доступ к переменной с именем listCount, она уже будет иметь значение из кода, который вы только что запустили, поэтому вы захотите создать ее заново.

person Evik James    schedule 10.06.2012
comment
Я думал, что смысл var в том, что он делает его локальным для функции, поэтому, если я попытаюсь получить доступ к переменной с именем listCount вне этого, она не будет существовать? - person Omiron; 10.06.2012
comment
Вам нужно var переменную при ее первом создании. Затем он будет привязан к контексту функции (и да, не будет существовать вне функции). - person Peter Boughton; 10.06.2012
comment
@Omiron, ты не показал свой код внутри функции. Вы упомянули функцию, но не показали свою функцию. Вот почему я не мог полностью ответить на ваш вопрос. - person Evik James; 11.06.2012
comment
Хорошо, извините, нужно было упомянуть, что код, который я вставил, был содержимым функции - person Omiron; 11.06.2012