2011-08-26 23 views
5

Sto scrivendo una funzione che scorre attraverso alcune informazioni su una pagina di registrazione. All'interno del ciclo sto provando a chiamare funzioni basate su un array. Quello che sto avendo problemi è in realtà chiamare correttamente le funzioni, perché sto cercando di incorporare una variabile come parte del nome della funzione.Come posso chiamare un nome di variabile come parte di un nome di funzione in ColdFusion?

Ecco il mio codice:

<cfscript> 
fields = arraynew(1); 
fields[1] = 'r_email'; 
fields[2] = 'r_uname'; 
fields[3] = 'r_pass'; 

for(i = 1; i lte arraylen(fields); i = i + 1) 
{ 
    func = fields[i].split('r_'); 
    func = 'validate_#func[2]#(#fields[i]#)'; 
} 
</cfscript> 

Così, ho tre funzioni: validate_email, validate_uname, validate_pass. Se lancio un writeoutput() e provo a generare i risultati della funzione, non funzionano.

Ecco il codice:

<cfscript> 
fields = arraynew(1); 
fields[1] = 'r_email'; 
fields[2] = 'r_uname'; 
fields[3] = 'r_pass'; 

for(i = 1; i lte arraylen(fields); i = i + 1) 
{ 
    func = fields[i].split('r_'); 
    func = 'validate_#func[2]#(#fields[i]#)'; 
    writeoutput('#func#'); 
} 
</cfscript> 

Ora, capisco che quando si sta utilizzando writeoutput(), e si sta chiamando una funzione, è necessario il simbolo cancelletto su entrambe le estremità. Quindi, diciamo che scrivo in questo modo:

writeoutput('#validate_#func[2]#(#fields[i]#)#'); 

Non funzionerà, perché il simbolo cancelletto secondo annulla la chiamata di funzione. Questo è come la funzione dovrebbe infine guardare (ad esempio e-mail):

writeoutput('#validate_email('[email protected]')#'); 

Come posso sostituire 'e-mail' (#validate_email ...) con il corretto nome della variabile, e hanno ancora il lavoro di funzione? Spero di averlo reso comprensibile!

risposta

10

Le funzioni sono variabili anche, in modo allo stesso modo è possibile utilizzare la notazione staffa per gli array, struct, e gli ambiti, si può usare questo per accedere ai nomi dinamici variabili (e nomi di funzione così dinamici)

Ad esempio:

<cfloop index="Field" list="email,uname,pass"> 
    <cfset Result = Variables['validate_'&Field](Variables['r_'&Field]) /> 
    ... 
</cfloop> 

Beh ... non proprio. A causa di un bug in Adobe ColdFusion, che non funziona così (anche se lo fa in altri motori di CFML, come Railo), e si deve dividere in due linee, in questo modo:

<cfloop index="Field" list="email,uname,pass"> 
    <cfset TmpFunc = Variables['validate_'&Field] /> 
    <cfset Result = TmpFunc(Variables['r_'&Field]) /> 
    ... 
</cfloop> 

(Questo si presuppone che sia la funzione che i campi siano nello scope variables, se non è necessario fare riferimento a qualsiasi ambito in cui si trovano.)

Questo metodo ha un problema se la funzione era in un oggetto con stato, perde il riferimento a quelle variabili.

Su CF10, c'è la funzione invoke. Le versioni precedenti di CF devono utilizzare il tag cfinvoke.


(Come nota a margine, CF10 ha fatto aggiungere la possibilità inversa referenziare risultati delle funzioni con la notazione staffa, cioè doSomething()[key] che viene in utile, a volte.)

+0

Non è un bug in CF, Peter: non è mai stato implementato. Potresti voler rivedere il tuo commento in tal senso. Altrimenti una risposta puntuale. –

+3

Non ci dovrebbero essere differenze tra 'Variables.Bob()' e 'Variables ['Bob']()' - tuttavia, il secondo lancia un errore. Io chiamo un bug nella notazione delle parentesi, non una caratteristica non implementata. –

+0

Grazie per questo. Entrambi i suggerimenti che ho ricevuto funzionano alla grande. Sono nuovo di CF, quindi vado con la folla e uso questo piuttosto che l'altro. L'altro è un po 'più facile, ma se c'è un problema con esso non voglio entrare in quello! Grazie ancora! –

1

risposta di Pietro è impeccabile (tranne la riferimento "bug" che ho commentato). Un'altra opzione, se non si è nel mezzo di un blocco CFScript, è che <cfinvoke> prende una stringa come valore dell'attributo METHOD e che, ovviamente, può assumere qualsiasi valore dinamico che si desideri.

Questo non è così utile per la tua situazione specifica, ma è utile tenerlo a mente.

NON utilizzerei l'approccio evaluate().

0

Aggiungerò che prima di ColdFusion 6.1 e il passaggio a Java, evaluate() è sempre stato considerato un anatema per le prestazioni, ma dal 6.1 tutto ciò che fa è valutare l'espressione e quindi creare un PageContext inline con la stringa inclusa come codice .

Molto lucido, molto performante.

Quindi, il modo più semplice e diretto per raggiungere i vostri obiettivi è:

<cfset result = evaluate("validate_#Field#(variables.r_#Field#)") /> 

Quindi a patto di avere un campo denominato "username", questo sarà l'equivalente di chiamata seguente metodo:

<cfset result = validate_username(variables.r_username) /> 

e qualunque cosa ritorni sarà assegnato a variables.result.

Nei test effettivamente supera la tecnica di notazione a staffa di riassegnazione di un metodo a un nuovo nome. Non ho statistiche qui al momento, ma è più veloce di un margine considerevole perché un inclusione è più veloce (e si verifica in una fase diversa di analisi/compilazione) rispetto a un'assegnazione variabile.

+0

Per quanto riguarda l'ammonizione di Adam rispetto a evaluate(), oltre ad essere stupido e utilizzarlo per valutare i valori forniti dall'utente (come consentire agli utenti di digitare essenzialmente il codice - sì, è stato fatto) non c'è motivo di evitare l'uso di questo metodo. Non ci sono persistenti problemi di prestazioni o di sicurezza. – jrypkahauer

0

Un altro approccio a questo problema è quello di utilizzare una funzione definita dall'utente come il seguente:

<cffunction name="callMethod"> 
     <cfargument name="methodName" type="string" required="true" /> 
     <cfargument name="methodArgs" type="struct" default="#{}#" /> 
     <cfset var rslt = 0 /> 
     <cfinvoke method="#arguments.methodName#" argumentcollection="#arguments.methodArgs#" returnvariable="rslt" /> 
     <!--- account for the possibility that the method call wiped out the rslt variable ---> 
     <cfif structKeyExists(local,'rslt')> 
      <cfreturn rslt /> 
     </cfif> 
    </cffunction> 

Se è necessario si potrebbe in questo modo:

<cfinclude template="invokerudf.cfm" /> 
    <cfscript>...</cfscript> 

o avvolgetelo in tag e lancia in una CFC:

<cfset retval = createObject("methodinvoker").callMethod(methodName,methodArgs) /> 

ci sono molti modi, molti a rendere questo accada ... basta lanciare varie idee là fuori ora.

Problemi correlati