Функционални локални променливи в 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.

Тъй като вече сте var обхванали променливата listCount, не е необходимо да го правите отново в същата функция - по избор можете да използвате <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 е unvaredstruct. добре ли е Ако първо направя 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