Creating a Structure As are arrays, structures are complex objects with a specific ization function, as the following example shows: initial- StructNewdoesn’t take any arguments, because
Trang 2Working with Structures
This chapter discusses structures, which are the most complexand most useful of the complex data types You learn what struc-tures are, how to use them, and when to use them
What is a Structure?
Simply put, a structure is a container for other variables Structures
are like arrays in that they contain programmatically addressable ments, but structures name each element rather than simply assign-
ele-ing them numeric positions Graphically, a structure looks as shown
remem-ments have names You see how naming each element is useful
throughout the chapter
Creating a Structure
As are arrays, structures are complex objects with a specific ization function, as the following example shows:
initial-<cfset myStruct = StructNew()>
StructNew()doesn’t take any arguments, because structures do nothave a simple dimension As you will see in the section “NestedStructures” later in this chapter, structures may have arbitrarily com-plex dimensions
If myStruct currently contains a value, StructNew()destroys the oldvalue and replaces it with an empty structure
In This Chapter
UnderstandingstructuresManipulating structurekeys
Visualizing complexstructures
The various notationsused in referencingstructures
Structure copyingcaveats and solutions
Trang 3334 Part III ✦ The ColdFusion MX Language
Adding an Element to a Structure
An element in a structure consists of a key and a value The key is the name of the element,
and the value is what the element contains Think of the package as if each element were avariable and the structure a named container of those variables
You can add an element to a structure in multiple ways, all of which we discuss in the ing sections
follow-StructInsert() vs StructUpdate()
One way to add an element to a structure is by using StructInsert(), as follows:
<cfset success = StructInsert(myStruct, “FavoriteFruit”, “apple”,
of an already existing key: If it is FALSE and the key already exists, ColdFusion throws anerror Note that the fourth argument defaults to FALSE if omitted
To modify an element that’s already in the structure, you can use StructUpdate(), as follows:
<cfset success = StructUpdate(myStruct, “FavoriteFruit”, “pineapple”)>StructUpdate()throws an error if the specified key doesn’t already exist in the structure.After you use StructUpdate(), myStruct looks as follows:
FavoriteFruit: pineapple
We usually use StructInsert() by passing TRUE for the fourth parameter, because that way,you don’t need to know whether the specified key already exists before creating it
Using dot notation
StructInsertand StructUpdate() are unwieldy Take, for example, the following statement:
<cfset success = StructInsert(myStruct, “SweetestFruit”, “peach”,
“TRUE”)>
Now compare it to the following:
<cfset myStruct.SweetestFruit = “peach”>
The second version is simpler and easier to read, and it does the same thing The only back to using dot notation is that you can’t embed special characters in the name of the key,
draw-as you can by using StructInsert(), draw-as shown in the following snippet:
Trang 4<cfset success = StructInsert(myStruct, “Biggest Fruit”, “watermelon”,
“TRUE”)>
<cfset myStruct.Biggest Fruit = “watermelon”>
Notice that the second CFSET statement would cause a syntax error You also can’t use dotnotation to create a key name that is a number, as follows:
<cfset myStruct.3182 = “some value”>
This statement throws an error You can use StructInsert() or StructUpdate() to createthis key
Another problem with dot notation is that it does not preserve the case of the key Supposethat you do the following:
<cfset someStruct.SomeKey = 1>
Here, someStruct contains a key named SOMEKEY, because ColdFusion converts all keynames created by using dot notation to uppercase It is unclear why this occurs, but the goodnews is that the uppercase keys will rarely affect your code because ColdFusion doesn’t con-sider case when looking up a key name
Using associative array notation
You have yet another syntax that you can use for creating a structure key, as the followingexample shows:
<cfset myStruct[“NotARealFruit”] = “zucchini”>
That syntax is equivalent to the following:
<cfset myStruct.NotARealFruit = “zucchini”>
This new syntax is called associative-array notation For an explanation, look at Figure 15-2.
Figure 15-2: Side-by-side comparison of an array and a
structure
Notice that the structure readout in the figure looks almost like two arrays sandwichedtogether — one array containing the element names and the other containing the element val-ues These two “arrays” are associated with one another so that referring to an element inone array retrieves the element in the other array (Note, however, that the names and valuesare not actually arrays; it’s just convenient to use arrays as a comparison.)
The first advantage to this notation is that you can now embed special characters in the keyname, as follows:
<cfset myStruct[“3182”] = “some value”>
<cfset myStruct[“Coolest Fruit”] = “cherry”>
Another advantage is that associative-array notation preserves the case of the key name Thissyntax truly is the best of both worlds — it’s as easy to use as dot notation but as flexible asStructInsert() Given the choice between dot notation and associative-array notation, thelatter is usually your best bet for creating structure keys
Trang 5336 Part III ✦ The ColdFusion MX Language
The real advantage to using an associative array, however, is the capability to use dynamickey names, as shown in the following example:
<cfset myKeyName = “Sourest Fruit”>
<cfset myStruct[myKeyName] = “lemon”>
After that last call, myStruct would look as follows:
FavoriteFruit: pineappleSweetestFruit: peachBiggest Fruit: watermelonNotARealFruit: zucchiniCoolest Fruit: cherry
Sourest Fruit: lemon
Notice that, instead of storing lemon in an element named myKeyName, ColdFusion evaluatesmyKeyNameand stores lemon in an element named Sourest Fruit
Retrieving an Element From a Structure
You have two ways to retrieve an element from a structure The simplest is to use dot tion or associative-array notation, as follows:
<cfset myStruct[“TangiestFruit”] = “orange”>
<cfoutput>#myStruct.TangiestFruit#</cfoutput>
We set elements by using associative-array notation to preserve the case of the key, but weretrieve the element by using dot notation because it’s typically more readable Remember,however, that you aren’t required to use only one syntax — use whichever syntax best fits thesituation
Removing an Element From a Structure
You delete a key from a structure by using StructDelete(), as follows:
<cfset success = StructDelete(myStruct, “Coolest Fruit”)>
After you call StructDelete(), myStruct looks as follows:
Trang 6FavoriteFruit: pineappleSweetestFruit: peachBiggest Fruit: watermelonNotARealFruit: zucchiniSourest Fruit: lemonTangiestFruit: orangeCoolest Fruitis gone Notice that you never have undefined keys in a structure, which isanother way that structures are different from arrays.
To quickly remove all elements from a structure, use StructClear(), as follows:
<cfset success = StructClear(myStruct)>
After you call StructClear, myStruct still exists, but it contains no elements
Getting Information About a Structure
StructIsEmpty()tells you whether any elements are in a given structure, as the followingexample shows:
IsStructreturns TRUE if the passed variable is a structure and FALSE if it does not
Looping Over a Structure
One form of CFLOOP enables you to loop over the keys in a structure, as the following ple shows:
exam-<cfoutput>
<cfloop collection=”#myStruct#” item=”theItem”>
The value of the #theItem# key is #myStruct[theItem]#.<br>
</cfloop>
</cfoutput>
The collection attribute tells ColdFusion which structure to loop over, and the itemattribute is what ColdFusion names the variable that contains the key name ColdFusionloops over every element in the structure and puts the key’s name in theItem each time that
the loop iterates
If you don’t need to loop over the structure but you need a list or array containing the ture’s keys, use StructKeyList() or StructKeyArray(), as follows:
struc-<cfset keyList = StructKeyList(myStruct)>
<cfset keyArray = StructKeyArray(myStruct)>
Trang 7338 Part III ✦ The ColdFusion MX Language
StructKeyList()takes an optional second parameter describing the delimiter to use for thelist The following line of code, for example, creates a semicolon-delimited list containing thenames of the top-level keys in myStruct:
<cfset keyList = StructKeyList(myStruct, “;”)>
Nested Structures
So far you’ve created keys containing simple string values, but the most powerful feature ofstructures is their capability to nest inside another structure Nesting enables you to create
hierarchical data structures that closely resemble real-world data models
The simple structure that we created earlier looks like what’s shown in Figure 15-3
Figure 15-3: A simple structure.
A nested structure would look like what’s shown in Figure 15-4
Figure 15-4: Structures nested inside one another.
Notice that in Figure 15-4, smaller structures are nested inside the enclosing structure.Creating a nested structure may seem complicated, but it really is quite simple In the follow-ing example, you create a new key named FruitCosts in myStruct that contains a substruc-ture that, in turn, contains the names of fruits and their respective costs:
Trang 8<cfset myStruct[“FruitCosts”] = StructNew()>
<cfset myStruct.FruitCosts[“Oranges”] = 1.99>
<cfset myStruct.FruitCosts[“Apples”] = 1.50>
<cfset myStruct.FruitCosts[“Peaches”] = 1.75>
Now myStruct looks like what’s shown in Figure 15-5
Figure 15-5: A substructure inside myStruct.
Structures can be nested many levels deep in any configuration
You can also use associative-array notation to create nested structures, as follows:
<cfset myStruct[“FruitCosts”] = StructNew()>
<cfset myStruct[“FruitCosts”][“Oranges”] = 1.99>
<cfset myStruct[“FruitCosts”]]”Apples”] = 1.50>
<cfset myStruct[“FruitCosts”][“Peaches”] = 1.75>
Our personal preference is to use dot notation for every nesting level except the final one, as
in the following example:
<cfset myStruct[“FruitCosts”] = StructNew()>
You can, for example, also nest arrays inside of structures, as follows:
<cfset myStruct[“FruitArray”] = ArrayNew(1)>
<cfset myStruct.FruitArray[1] = “orange”>
<cfset myStruct.FruitArray[2] = “lemon”>
<cfset myStruct.FruitArray[3] = “pineapple”>
After you add the array, myStruct looks like what’s shown in Figure 15-6
Trang 9340 Part III ✦ The ColdFusion MX Language
Figure 15-6: An array nested inside myStruct.
Structures can be nested any number of levels deep In addition, array elements can containstructures, as the following example shows:
<cfset myArray = ArrayNew(1)>
<cfset myArray[1] = StructNew()>
<cfset myArray[1][“MyTestKey”] = “my value”>
Listing 15-1: Creating a shopping cart in a structure
<cfset myCart = StructNew()>
<cfset myCart.cartID = CreateUUID()>
<cfset myCart.saleDate = Now()>
<cfset myCart.arItemNumber = ArrayNew(1)>
<cfset myCart.arItemName = ArrayNew(1)>
<cfset myCart.arQuantity = ArrayNew(1)>
<cfset myCart.arUnitPrice = ArrayNew(1)>
<cfset myCart.arExtended = ArrayNew(1)>
Trang 10Later on, after your shopping cart contains line items in its arrays, you can easily producefamiliar values, as the following list describes:
✦ The subtotal is simply ArraySum(myCart.arExtended),
✦ The sales-tax amount is simply (myCart.salesTaxRate * myCart.subtotal)
✦ The total is (myCart.subtotal + myCart.salesTaxAmount +myCart.shippingAmount)
You face no limits on how much information that your structure can store, nor do you haveany limits as to how complex it can be The only thing that you must watch out for is makingyour structure too complicated for your needs You can usually keep track of what goes
where quite easily if you make the format of your structures emulate the real-world data thatyou are storing
How dot notation automatically creates a nested structure
Until now, you’ve created structures only by using StructNew() You can also create tures just by using dot notation, as follows:
struc-<cfset aNewStruct.SomeKey.SomeValue = 1>
That line creates a structure named aNewStruct with a substructure named SomeKey thathas an element named SomeValue with a value of 1 You can visualize it as what’s shown inFigure 15-7
Figure 15-7: A structure created by using dot notation.
Notice that, in the figure, the names of all newly created structures and keys are all case, regardless of the capitalization that you use in the code
upper-As convenient as this method may seem, it does have a drawback Consider the followingsnippet:
<cfset aNewStruct = StructNew()>
Trang 11342 Part III ✦ The ColdFusion MX Language
Suppose that you now attempt to call the following snippet to create a nested structurenamed myKey:
<cfset aNewStruct.myKey.subKey = 4>
If the previous line had worked, it would have overwritten the previous simple value of myKeywith a substructure named myKey, and aNewStruct would look as shown in Figure 15-9.
Figure 15-9: aNewStruct with a nested substructure.
Fortunately, however, attempting to overwrite a simple key with a substructure throws anerror in ColdFusion MX
You do face a limitation in creating structure keys by using this method ColdFusion MX cancreate nested structures only up to three levels deep in a single call, as in this example:
<cfset a = StructNew()>
<cfset a.b.c.d = “some value”>
Attempting to create a key more than three levels deep gives you unexpected results.ColdFusion ignores all but the last three keys Running the following code, for example, out-puts “some value”:
Say that you have a structure containing the prices per pound of different fruits, as following:
<cfset FruitCosts = StructNew()>
Trang 12Listing 15-2: Looping over a structure of fruit prices
Listing 15-2 would produce the output shown in Figure 15-10
Figure 15-10: The output of Listing 15-2.
The problem here is that structures don’t have any kind of inherent order Structure keys arestored in an internal order that only ColdFusion Server understands, and if you loop over thestructure, that’s the order that you see The order in which you insert the keys doesn’t evenmatter
StructSort()returns an array of key names sorted by their values (not their key names).
Listing 15-3 uses StructSort() to put the fruit costs out in order
Listing 15-3: Ordering the structure before looping
<cfset keyArray = StructSort(FruitCosts, “numeric”, “asc”)>
Listing 15-3 produces the output shown in Figure 15-11
Figure 15-11: The output of Listing 15-3.
The biggest difference between Listings 15-2 and 15-3 is the approach that each takes to ing over the structure In Listing 15-2, we just use a standard collection loop, relying on what-ever order ColdFusion stored the structure in In Listing 15-3, we call StructSort() first,which returns an array of key names that look as follows:
Trang 13loop-344 Part III ✦ The ColdFusion MX Language
1: Apples2: Lemons3: Peaches4: Oranges5: CherriesThese elements still may not seem to be in any particular order, but look at the followingprices associated with each element (although the values in parentheses are not actually part
of the array):
1: Apples (1.50)2: Lemons (1.65)3: Peaches (1.75)4: Oranges (1.99)5: Cherries (2.25)Although the key names appear in the array, the values remain back in the structure
After calling StructSort(), Listing 15-3 loops through keyArray, which contains the sortedkey names During this loop, keyArray[i] contains the current key name, which can in turn
be used to supply the key name to the FruitCosts structure If you follow the ColdFusionprocessing engine along step-by-step, the resolution of this reference is as follows:
Step 1: #FruitCosts[keyArray[i]]#
Step 2: #FruitCosts[keyArray[1]]#
Step 3: #FruitCosts[“Apples”]#
Result: 1.50But what if you want to sort by a key in a nested structure? We’ve modified the FruitCostsstructure from earlier in the chapter by using the following code:
<cfset FruitCosts = StructNew()>
<cfset FruitCosts[“Oranges”] = StructNew()>
Trang 14Figure 15-12: A structure with fruit prices by the pound and by the sack.
So now that you have this set of nested structures, how do you sort by price per pound?
A fourth attribute of StructSort()describes a dot path to the sort value, as shown in Listing 15-4
Listing 15-4: Sorting by a nested key
<cfset keyArray = StructSort(FruitCosts, “numeric”, “asc”, “lb”)>
Listing 15-5: Sorting by a different nested key
<cfset keyArray = StructSort(FruitCosts, “numeric”, “asc”, “sack”)>
<cfoutput>
<cfloop from=”1” to=”#ArrayLen(keyArray)#” index=”i”>
#keyArray[i]#: #FruitCosts[keyArray[i]].sack# / sack.<br>
</cfloop>
</cfoutput>
Trang 15346 Part III ✦ The ColdFusion MX Language
You can sort by a key any number of levels deep by adding elements to the dot path:
<cfset keyArray = StructSort(FruitCosts, “numeric”, “asc”,
Consider the following snippet:
<cfset myVar = 1>
<cfset yourVar = myVar>
<cfset myVar = 2>
In these three lines, you have two simple variables: myVar, containing 1, and yourVar,
con-taining 2 If you set yourVar equal to myVar, you make a copy of myVar, so setting myVar to adifferent value doesn’t affect yourVar This is true for almost every variable type in
ColdFusion — assigning one variable to another makes a copy and then divorces the two ables so that making a change in one doesn’t make a change in the other
vari-Structures, however, are the one variable type where such simple copying doesn’t apply Saythat you call the following snippet, which creates a structure named myStruct, populates itwith a couple values, and then copies myStruct into a new structure named yourStruct:
<cfset myStruct = StructNew()>
<cfset myStruct[“aKey”] = 1>
<cfset myStruct[“bKey”] = 2>
<cfset yourStruct = myStruct>
<cfset myStruct[“aKey”] = 3>
You would expect to end up with myStruct.aKey equal to 3 and yourStruct.aKey still equal
to the original value of 1, but Figure 15-13 shows what really happens.
Figure 15-13: Two structures after attempting to copy and modify one.
What’s going on here? Both structures show the same value for aKey even though youchanged it only in myStruct This happens because of the way that you attempted to copymyStruct, as follows:
<cfset yourStruct = myStruct>
Trang 16Unlike any other ColdFusion data type, structures are accessed by reference and not by value.
If you access a variable by value, the variable is the actual value If you access a variable by
reference, the variable is a pointer to the value, which is stored somewhere else in memory.
Suppose that you call the following:
<cfset yourStruct = myStruct>
You aren’t copying the value — you’re copying the reference yourStruct, therefore, is now
pointing to the same object as myStruct, so changing something about myStruct changesyourStructas well — because they’re really the same structure In fact, the opposite holdstrue as well: Changing the value of yourStruct.aKey changes the value of myStruct.aKey tothe same value
If you want to copy myStruct to yourStruct so that the values in each structure are nolonger linked to one another, use StructCopy(), as follows:
<cfset yourStruct = StructCopy(myStruct)>
Now, making a modification to myStruct doesn’t affect the contents of yourStruct
Well, not exactly Things get a little confusing here, so hold on tight
Say that you change the contents of myStruct to include a nested structure, as follows:
<cfset myStruct = StructNew()>
yourStruct’svalue This situation is shown in Figure 15-14
Figure 15-14: Copying structures by using StructCopy().
Trang 17348 Part III ✦ The ColdFusion MX Language
This result happens because of how StructCopy() copies a structure’s members As thecode copied myStruct to yourStruct, it copied aKey and bKey by value (Remember thatthis means that aKey and bKey refer to actual values in both structures.) StructCopy(),however, still copies nested structures by reference, meaning that it copies the pointer to thestructure rather than the actual structure itself As such, both myStruct.aSubStruct andyourStruct.aSubStructstill point to the same structure after a StructCopy()
To completely divorce myStruct and all its substructures from yourStruct and all its
sub-structures, you must use Duplicate(), as follows:
<cfset myStruct = StructNew()>
Figure 15-15: Using Duplicate() to make a “deep copy.”
Nine times out of ten, you use Duplicate() to copy a structure At times, the other twomethods can prove useful, but those times are rare and typically very advanced Just beaware of exactly what your code does in duplicating a structure
Using Variable Scopes as Structures
You may not realize it, but you were using structures as far back as Chapter 2! You mayremember the following code from that chapter:
Trang 18<cflocation url=”Finished.cfm?msg=#URLEncodedFormat(‘Company ID
#Form.CompanyID# has been deleted from the database.’)#” addtoken=”No”>
Form.CompanyIDis actually an element in a structure! All the ColdFusion variable scopes areactually structures: Form, URL, Client, and so on This may not seem very interesting until youthink about what you can do with structures You can get a list of all of a request’s form vari-ables by using StructKeyList() You can delete a local variable by using StructDelete()
You can even sort all of a request’s incoming URL variables by value by using StructSort()
You should, however, never do one thing with a variable scope: Never call StructClear() onthe Session scope Calling StructClear() on the Session scope clears the CFID, CFTOKEN, andURLTokenvariables, possibly causing problems within ColdFusion server Refer to MacromediaTechNote 14143 in the Macromedia KnowledgeBase for more information on this issue
Other Structure Operations
The techniques that you learn in the preceding sections of this chapter are all useful, andyou’re likely to use most if not all of them at some point in your development career Thetechniques in this section are advanced, but that’s not to say that you shouldn’t learn them
You may eventually run into a situation where these techniques are just what the doctorordered
Merging two structures
If you have two related structures, you may decide to group them together You can create a
superstructure, which simply sets the two structures as subkeys of a larger one You can also
take the keys from one structure and transplant them into the other
The simplest technique is to create a superstructure Consider the following snippet:
<cfset myStruct = StructNew()>
<cfset ourStruct = StructNew()>
<cfset ourStruct[“myStuff”] = myStruct>
<cfset ourStruct[“yourStuff”] = yourStruct>
ourStructhas grouped together the contents of myStruct and yourStruct ourStructlooks like what’s shown in Figure 15-16
Figure 15-16: ourStruct as a superstructure.
Trang 19350 Part III ✦ The ColdFusion MX Language
Another option is to roll the keys from one structure into another by using StructAppend(),
<cfset StructAppend(myStruct, yourStruct)>
myStructnow contains the keys from yourStruct, as shown in Figure 15-17
Figure 15-17: myStruct after you call StructAppend().
Finding a key or value deep in a nested substructure
Two functions enable you to search for a key or value within a structure StructFindKey()and StructFindValue() return an array with elements that are structures describing thematched key or value
Consider the following call to the FruitCosts array from the section “[name section],” lier in this chapter:
ear-<cfset resultsArray = StructFindKey(FruitCosts, “lb”, “ALL”)>
resultsArraywould look as shown in Figure 15-18:
Trang 20Figure 15-18: The results of calling StructFindKey() to find all
prices per pound
Trang 21352 Part III ✦ The ColdFusion MX Language
In the figure, value is the value of the found key; owner is a reference to the structure that
“owns” the found key; and path is the dot path to the found key StructFindValue() returnsthe same results, but it searches by the value of the key rather than by the name of the key
Summary
We can think of no way to simplify the details of creating and manipulating structures anymore than how we’ve described them in this chapter, and the subject is still a hard one to master Go back and read the stuff that’s still a bit hazy, and by all means, play with the exam-ple code that we discussed in the chapter
Structures are widely regarded as the most difficult feature of ColdFusion, but they are thebasis for some of the best programming techniques in ColdFusion In this chapter, youlearned what you can do with structures and how to use them effectively
The next chapter covers CFSCRIPT, which enables you to use ColdFusion as an efficientscripting language
Trang 22Scripting ColdFusion with CFSCRIPT
One of the great things about ColdFusion is its easy-to-use, based syntax Having the capability to intersperse CFML andHTML tags without needing to open and close script blocks or needing
tag-to remember what syntax you’re currently working in is a wonderfulthing
At times, however, using a scriptable syntax would be nice — forexample, if you’re doing heavy data processing on a page As nice asCFML’s tag-based syntax is, number crunching is best expressed inscript
CFSCRIPTis a server-side scripting language that works with CFML togive you the best of both worlds: an elegant, tag-based syntax when-ever you need it and a flexible scripting syntax if you don’t
User-defined functions are discussed in Chapter 17 We do notinclude them in this chapter because of ColdFusion MX’s capabil-ity to define UDFs by using either CFSCRIPT or CFML syntax Thischapter describes the basics of CFSCRIPT
to code and faster to process
Consider the following snippet, for example, which loops from 1 to
10, adding each index to a running total:
<cfset TheSum = 0>
<cfloop from=”1” to=”10” index=”i”>
<cfset TheSum = TheSum + i>
Setting variables byusing CFSCRIPTFlow control by usingscripting syntax
Trang 23354 Part III ✦ The ColdFusion MX Language
Look familiar? If you’re having JavaScript déjà vu, that’s normal, because CFSCRIPT is almost
identical to JavaScript CFSCRIPT blocks mostly instantiate variables and perform tions, but they can produce output, too Consider, for example, the following extension of thepreceding code snippet:
calcula-<cfset TheSum = 0>
<cfloop from=”1” to=”10” index=”i”>
<cfset TheSum = TheSum + i>
The difference between WriteOutput() and document.write() shows the different mindset
of CFSCRIPT as compared to CFML Rather than the text just “being there,” as it is in CFML,CFSCRIPT must be directed to output the text
Notice the use of semicolons, which terminate CFSCRIPT statements CFSCRIPT is less ing than JavaScript with respect to semicolon termination; forget one, and ColdFusion throws
forgiv-an error
Another thing that you need to get used to in CFSCRIPT is the use of curly braces Curlybraces surround blocks of CFSCRIPT code similar to the way that opening and closing tagssurround blocks of CFML code, but their use is actually more critical A simple if construct
in CFSCRIPT looks as follows:
<cfscript>
if(this EQ that)doThis();
elsedoThat();
Trang 24if(this EQ that)doThis();
doSomeOtherThingToo();
elsedoThat();
</cfscript>
ColdFusion throws an error, because it sees an else clause without a corresponding ifclause This happens because the doSomeOtherThingToo() statement is considered the con-
tinuation of statements after the if statement finishes executing the doThis() statement For
CFSCRIPT to execute both statements when the if tests TRUE, you must enclose them withincurly braces as follows:
In fact, a best practice is to always include the curly braces, regardless of whether you need
them or not, as in the following example:
How to use CFSCRIPT
All CFSCRIPT code is contained between CFSCRIPT tags, as shown in the following example:
Regular CFML code goes here
<cfscript>
CFSCRIPT code goes here
</cfscript>
Regular CFML code goes here
Notice that the content of a CFSCRIPT block must be a complete statement You cannot, for
example, do the following:
<cfscript>
if(myVar GT yourVar) {
Trang 25356 Part III ✦ The ColdFusion MX Language
Setting variables in CFSCRIPT
The simplest operation in CFSCRIPT is setting a variable, as follows:
vari-If constructs in CFSCRIPT
The if construct in CFSCRIPT works exactly the same as its CFML counterpart, CFIF.ColdFusion evaluates a condition and executes a dependant statement based on whether thecondition is true or false
Compare the following CFML and CFSCRIPT if constructs:
✦ CFML:
<cfif myVar GT yourVar>
Trang 26if(myVar GT yourVar) { execute if true
}
else {
execute if false
}You also find an equivalent to CFELSEIF in CFSCRIPT, as follows:
if(myVar GT yourVar) { execute if true
}
else if (myVar EQ yourVar) {
execute if true
}else { execute if all conditions false
}
Switch constructs in CFSCRIPT
CFSCRIPT has an equivalent to CFSWITCH, but you find some important differences Take, forexample, the following CFSWITCH statement:
Execute if myVar equals 1
break;
case 2:
case 3:
Trang 27358 Part III ✦ The ColdFusion MX Language
Execute if myVar equals 2 or 3
break;
default:
Execute if no other case executes
}These snippets are very different, but they do the same thing Both evaluate myVar and thenexecute whichever case statement matches If none of the statements match, ColdFusion exe-cutes the default section
Look at the differences in syntax between the tag-based and script-based versions Ratherthan each case being in its own enclosed in a paired tag (<CFCASE></CFCASE>), each casedeclaration (for example, case 1:) is followed by dependent statements and terminated by abreakstatement Without that break statement, the case declaration remains open, so exe-cution of that case’s dependent statements continues
The use of explicit break statements in CFSCRIPT emulates CFCASE’s capability to use acomma-delimited list of match values If myVar equals either 2 or 3 in the preceding snippet,for example, the same dependent statements are executed until a break is reached
Looping constructs in CFSCRIPT
Looping constructs in CFSCRIPT are rather different from those in CFML You have the ing five types of CFML loops:
follow-✦ Index loops (also known as For loops), which loop a specified number of times.
✦ Conditional loops, which loop as long as a condition tests true.
✦ Query loops, which loop over each row in a query.
✦ List loops, which loop over each element in a list.
✦ Structure loops, which loop over each top-level key in a structure.
You have only the following four types of CFSCRIPT loops:
1 For loops, which loop a specified number of times.
2 While loops, which loop as long as a condition tests true.
3 Do-While loops, which loop at least once and then continue looping as long as a
condi-tion tests true
4 Structure loops, which loop over each top-level key in a structure.
CFSCRIPT’s While loop is the direct equivalent of CFML’s Conditional loop, but CFML has nodirect equivalent to the CFSCRIPT Do-While loop
The following sections look at each of these four types of loops
Looping a set number of times
The For loop is akin to CFLOOP using the from, to, index, and step attributes In CFSCRIPT it
is structured like this:
Trang 28for(indexVar = 1; indexVar LTE 10; indexVar = indexVar + 1) { body of loop
}One of the disorienting things about CFSCRIPT is its lack of attribute names Whereas inCFML, you can clearly identity the index variable, step increment, and other attributes ofthe loop, you now must positionally deconstruct the syntax in CFSCRIPT Doing so is easierthan you think, however The following code snippet uses CFLOOP attribute names inCFSCRIPT syntax to help you compare them to one another:
Looping while a condition is true
You have two different types of conditional loop in CFSCRIPT The first is a While loop, asshown in the following example:
bLoop = TRUE;
while(bLoop EQ TRUE) {
WriteOutput(“This is one iteration through the loop.<br>”);
if(RandRange(1, 10) EQ 10) {bLoop = FALSE;
}else {bLoop = TRUE;
}
} while(bLoop EQ TRUE);
These two loops are both conditional loops, but they work differently The While loop
evalu-ates its condition before executing the body of the loop, meaning that, if the condition is false
before the loop begins, the loop never executes
Conversely, the Do-While loop evaluates its condition after the loop body, meaning that the
loop body always executes at least once
Trang 29360 Part III ✦ The ColdFusion MX Language
Looping over a structure
Another type of loop that CFSCRIPT carries over from CFML is the Structure loop, whichloops over the top-level keys in a structure and looks as follows in CFML:
<cfloop collection=”#myStruct#” item=”theKey”>
<cfoutput>#theKey#: #myStruct[theKey]#<br></cfoutput>
</cfloop>
The CFSCRIPT version is similar, as the following example shows:
for(theKey in myStruct) {
WriteOutput(“#theKey#: #myStruct[theKey]#<br>”);
}
Breaking out of a loop
CFSCRIPT also has an equivalent to CFBREAK, as the following example shows:
for(i = 1; i LTE 10; i = i + 1) {if(RandRange(1,10) EQ 10) {
break;
}WriteOutput(“#i#<br>”);
}
In this example, ColdFusion normally ends the loop after iterating ten times If RandRange()ever returns 10 during the execution of this loop, however, the break statement ends theloop prematurely, immediately skipping past the end of the loop
Skipping an iteration of a loop
CFSCRIPT’s continue statement instructs ColdFusion to skip the rest of the current loop ation and immediately begin the next iteration of the loop, as shown in the following example:for(i = 1; i LTE 10; i = i + 1) {
iter-if(RandRange(1,10) EQ 10) {
continue;
}WriteOutput(“#i#<br>”);
Trang 30This is a multi-line comment
Both of these lines are commented
*/
</cfscript>
Single-line comments begin with two forward slashes — the entire line after the slash marks iscommented out, meaning that any code in that line does not execute
Multiline comments are surrounded by /* and */ — everything in between those two marks
is a comment You cannot nest multiline comments inside one another, but you can nest a single-line comment inside a multiline comment
Advantages of CFSCRIPT
CFSCRIPT has the following two primary advantages:
✦ It is often more understandable to use CFSCRIPT than CFML in processing data,because CFSCRIPT’s procedural syntax is often clearer than that of CFML
✦ You get a healthy performance gain form using CFSCRIPT CFML is basically an based language and, as such, must be parsed by an XML parser and executed by aninterpreter, and both operations are relatively slow CFSCRIPT requires only a singleXML-based tag pair to be parsed, after which its contents — essentially a simple list ofpreparsed commands — are passed to the processing engine all at once
XML-Sometimes entire pages of code are written in CFSCRIPT The best practice is to analyze yourtemplates to determine whether you can group together large blocks of code that don’trequire CFML tags and then write that code as a single CFSCRIPT block Sometimes you mustinterweave CFSCRIPT blocks with CFML, in which case you should try to minimize the num-ber of CFSCRIPT blocks that you create in a single template
Summary
In this chapter, you learn that CFSCRIPT is a powerful addition to the ColdFusion language and
is useful for use in large blocks of code that do not require the use of tag-based CFML The tax is much simpler than that of tag-based CFML for variable assignments and function calls,and although the syntax for flow-control constructs like if, switch, and the various loops mayseem difficult to grasp at first, your payoff is faster execution of more efficient code
Trang 32Building Defined Functions
User-ColdFusion has more than 250 functions in its language It hasstring-manipulation functions, array functions, structure func-tions, and many other types of functions Even with this wide variety
of functions, however sometimes you may want to define your ownfunction that does things that the built-in ColdFusion functions can’t
This chapter does not describe how to use CFSCRIPT — only how
to use CFSCRIPT in conjunction with user-defined functions
CFSCRIPT itself is discussed as a separate subject in Chapter 16
Building UDFs by Using CFSCRIPT
A user-defined function, or UDF, can be built in CFSCRIPT, CFML, or a
combination of the two The following sections describe how to buildUDFs by using CFSCRIPT CFSCRIPT UDFs are very natural becausethey mimic function creation in JavaScript, Java, and many other pro-gramming languages which you may already be familiar with
UDF structure
In general, functions receive one or more arguments and return a
sin-gle result Some functions (such as ColdFusion’s Now() function) donot take arguments, but almost all functions do return a result
A basic UDF built by using CFSCRIPT looks as follows:
<cfscript>
function GetCurrentTime() { return TimeFormat(Now(), “h:mm:ss tt”);
}
</cfscript>
The only four things required for every function are the functionkeyword, the name of the function, the parentheses after the name,and the curly braces around the body of the function Although thereturnstatement is technically optional, return is what makes afunction truly useful, as this statement represents the result returned
to the code that calls the function We call the function that we justcreated as we would any built-in ColdFusion function, as follows:
user-Building UDFs by usingCFFUNCTION
Working with functionarguments
Choosing a functioninvocation methodPlanning functionlibraries
Trang 33364 Part III ✦ The ColdFusion MX Language
We can expand my function by defining a local variable, as follows:
function GetCurrentTime() {
var szTime = TimeFormat(Now(), “h:mm:ss tt”);
return szTime;
}More on the var keyword in the following section We can also add code to our functionbetween the variable declaration and the return statement, as follows:
function GetCurrentTime() {
var szTime = TimeFormat(Now(), “h:mm:ss tt”);
szTime = ReplaceNoCase(szTime, “am”, “in the morning”);
szTime = ReplaceNoCase(szTime, “pm”, “in the evening”);
return szTime;
}This UDF is a very simple example that doesn’t take any arguments You learn how to buildUDFs that take arguments in the section “Defining function arguments,” a little later on in thischapter
The var keyword
Now to take a look at var in more detail var declares a variable that’s local to a function If
we didn’t use var, for example, we could do the following:
<cfscript>
function myFn() {myVar = 1;
Trang 34Now, attempting to use myVar outside myFn() throws an error, which is exactly what youwant it to do.
All variables created by using var must be initialized; ColdFusion throws an error if they arenot And notice, too, that you cannot place the var keyword anywhere other than at the verytop of a function declaration
The return keyword
Functions return a single value, as the following example shows:
return Client.myVar;
} else {
return TimeFormat(Now(), “h:mm:ss tt”);
}}Every control path in a function must return a value or you get inconsistent results A bestpractice, therefore, is to define a default return value and use only one return statement inyour function declaration, as follows:
function myFn() {var result = TimeFormat(Now(), “h:mm:ss tt”);
if(IsDefined(“Client.myVar”)) {result = Client.myVar;
}
return result;
}
Defining function arguments
Most functions take one or more arguments, as the following example shows:
<cfscript>
function add2(firstNumber, secondNumber) {
return firstNumber + secondNumber;
Trang 35366 Part III ✦ The ColdFusion MX Language
The argument names are defined in a list inside the parentheses after the function tion You see more advanced uses of arguments in the section “Using the ArgumentsCollection,” later in this chapter
declara-The preceding example used positional arguments, meaning that the first argument in the
function call was passed to the first argument in the function, the second in the call becamethe second in the function, and so on You can also name the arguments in the call if you want
to pass them in a different order, as follows:
<cfoutput>
#add2(secondNumber=2,firstNumber=1)#
</cfoutput>
This syntax is not widely used because it is slightly harder to read; it can help you, however,
if you don’t remember the order of a function’s parameters If any of a function call’s ments are named, however, all the arguments must be named
argu-Calling one function from another
Functions can be called anywhere within a ColdFusion template, even from within anotherColdFusion function Take the following example:
<cfscript>
function myFn(myNum, yourNum) {return myNum * yourNum;
}function myOtherFn() {var num1 = RandRange(1,10);
var num2 = RandRange(1,10);
return myFn(num1, num2);
}
</cfscript>
Calling functions recursively
A function can also call itself We wrote the following function, for example, to calculate the
factorial of a number (a factorial will be defined in a moment):
<cfscript>
function Factorial(myNum) {
if(myNum EQ 1) {return 1;
}else {return myNum * Factorial(myNum - 1);
}}
</cfscript>
<cfoutput>#Factorial(6)#</cfoutput>
The factorial of 6, for example, is 6 x 5 x 4 x 3 x 2 x 1, or 720 To calculate the factorial, we
return the number passed to the function, multiplied by the next smaller factorial (becausethe 6 factorial can also be represented as 6 x 5 factorial)
Trang 36You must be careful in creating recursive functions to make sure that you have a stop
tion In our case, we stop the recursion whenever myNum is 1 If you don’t build a stop
condi-tion into your logic, you put the request into an infinite loop
Passing structures to functions
In Chapter 15, we show you that structures are accessed by reference, whereas other ables are referenced by value The same applies to passing variables to a function If you pass
vari-a number to vari-a function, the function hvari-as vari-a locvari-al copy of thvari-at number, vari-and vari-any modificvari-ationsthat functions makes to the number are not repeated outside the function call
Structures are passed to a function by reference, however, so any modifications that the tion makes to the structure parameter can be seen outside the function call
func-Building UDFs by Using CFFUNCTION
The chief drawback to building UDFs by using CFSCRIPT is that you can’t use ColdFusion tags
in them If you need to do a database call while in a CFSCRIPT block, you must exit theCFSCRIPTblock and use a ColdFusion tag, so that tag cannot be included in the CFSCRIPTfunction definition
In MX, you can also define functions by using the CFFUNCTION tag, which does enable you to
use virtually any CFML tag in the declaration of a function Following is the CFML version ofGetCurrentTime()from the section “UDF structure,” earlier in the chapter:
<cffunction name=”GetCurrentTime” returntype=”string”>
<cfset var szTime = TimeFormat(Now(), “h:mm:ss tt”)>
<cfreturn szTime>
</cffunction>
The CFFUNCTION version of this UDF is very similar to its CFSCRIPT counterpart: You givethe function a name by using CFFUNCTION; you return a value by using CFRETURN; and youhandle all the business logic in between
The var keyword
Use of the var keyword in a CFFUNCTION function is identical to its use in a CFSCRIPT tion All local variables must be defined before any function code is executed, and all localvariables must be initialized
func-CFRETURN
The only difference between the CFRETURN tag and CFSCRIPT’s return keyword is the syntax
Both return a single value
However, CFFUNCTION has an optional returntype attribute, which specifies the type of datathat the function must return If CFRETURN does not return a value of the specified type,ColdFusion throws an error
Trang 37368 Part III ✦ The ColdFusion MX Language
Defining function arguments
The main difference between CFFUNCTION and CFSCRIPT functions is how arguments aredefined CFSCRIPT just places argument names in an ordered list after the function name, butCFFUNCTIONmore clearly defines its arguments by using the CFARGUMENT tag:
<cffunction name=”add2” returntype=”numeric”>
<cfargument name=”firstNumber” type=”numeric”>
<cfargument name=”secondNumber” type=”numeric”>
<cfreturn firstNumber + secondNumber>
</cffunction>
You can still pass arguments to CFFUNCTION-declared UDFs positionally and without namingthem, just as you would with CFSCRIPT UDFs If you do, CFFUNCTION matches arguments inthe order that they are passed to each declaration of arguments in the UDF
Similarly, you can pass named arguments in an order that’s different from that in which theyare declared in CFFUNCTION, just as you can with CFSCRIPT UDFs
Required arguments
One benefit to writing UDFs by using the new CFFUNCTION syntax is the capability to specifywhich arguments are required and which are optional — something that you cannot do withCFSCRIPT UDFs
If we want the firstNumber argument to be required, for example, we write the UDF as lows:
fol-<cffunction name=”add2” returntype=”numeric”>
<cfargument name=”firstNumber” type=”numeric” required=”Yes”>
<cfargument name=”secondNumber” type=”numeric”>
<cfreturn firstNumber + secondNumber>
</cffunction>
If you leave the required attribute off a CFARGUMENT, it defaults to an optional argument(required=”No”)
Choosing Between CFSCRIPT and CFFUNCTION
In this chapter, you’ve learned about two different ways to create functions in ColdFusion, butwhich one should you choose?
The easiest answer is to use CFSCRIPT unless you must call a CFML tag Because CFFUNCTIONcan call any tag, including CFQUERY or CFSTOREDPROC, it’s the only option for doing databasework in a UDF On the other hand, if all you’re doing is processing variables, you can useCFSCRIPT’s simpler syntax
What’s more, you can call one type of function from the other Following is an example withone of each type of function showing a valid way for the two to interact:
<cffunction name=”GetEmployeesFromDB” returntype=”Query”>
<cfset var GetEmployees = “”>
<cfquery name=”GetEmployees”
datasource=”CFMXBible”>
Trang 38SELECT Salary, SSNFROM Employee
Note that in the preceding example we seem to violate our own guideline by setting variables
without using var, but here it’s actually valid Because we’re expecting that the calling code
will use TheMax, TheMin, and TheSum, it’s OK to set these without using var Do note, ever, that it is very rare that this technique is valid In fact, some would argue that instead ofsetting TheMax, TheMin, and TheSum, we should have created a structure to contain all threevalues and returned the single structure
how-Using the Arguments Collection
Typically, you individually reference each argument passed to a function by its name, as follows:
<cffunction name=”UseNamedArgs” returntype=”numeric”>
<cfargument name=”arg1” type=”numeric” required=”yes”>
<cfargument name=”arg2” type=”numeric” required=”yes”>
<cfset var result = Arguments.arg1 + Arguments.arg2>
<cfreturn result>
</cffunction>
But referring to arguments by their names isn’t the only method that you can use to accessargument values
Trang 39370 Part III ✦ The ColdFusion MX Language
Whenever you pass arguments to a function, ColdFusion creates an object named Arguments
that contains this collection of arguments We say collection rather than structure or array
because Arguments is different from any other ColdFusion object: It can behave as either a
structure or an array under certain circumstances.
If your arguments are named in either the declaration of the function or as the function iscalled, a structure named Arguments that contains keys named for those arguments is cre-ated, and you can CFDUMP this structure to inspect it Calling the preceding snippet, for exam-ple, creates an Arguments structure containing two keys named arg1 and arg2, and theirvalues are equal to those passed for each argument
Suppose, however, that you don’t name your arguments in the declaration of the function or
as it is called, as is the case with the following example:
<cffunction name=”UseUnnamedArgs” returntype=”numeric”>
<cfset var result = ArrayLen(Arguments)>
<cfreturn result>
</cffunction>
<cfoutput>#UseUnnamedArgs(5,11)#</cfoutput>
In this case, ColdFusion creates an Arguments object that cannot be inspected with CFDUMP
(and you can’t even loop over it using a Collection loop) but can be used the same as an
array So you can reference the individual arguments as Arguments[1], Arguments[2], and
so on, and you can also use ArraySum(Arguments), ArrayMin(Arguments), and so on.Following is an example of when you’d use Arguments as an array:
<cfscript>
function SumUnnamedArgs() {return ArraySum(Arguments);
Calling a function by Using CFINVOKE
In the preceding sections, you’ve called functions only by using a syntax such asSumArgs(8,3,4,2) You can, however, also call functions by using two CFML tags: CFINVOKEand CFINVOKEARGUMENT The following code, for example, is a function that we used in thesection “Defining function arguments,” earlier in the chapter:
<cfscript>
function add2(firstNumber, secondNumber) {
return firstNumber + secondNumber;
}
</cfscript>
Trang 40Following is how we would call that function by using the standard syntax:
<cfoutput>#add2(1,2)#</cfoutput>
If we call that function by using CFINVOKE, it looks as follows:
<cfinvoke method=”add2” returnvariable=”theSum”>
<cfinvokeargument name=”firstNumber” value=”7”>
<cfinvokeargument name=”secondNumber” value=”3”>
</cfinvoke>
<cfoutput>#theSum#</cfoutput>
Notice that, in using CFINVOKE, we specify the name of the variable in the calling templateinto which the result of the function is returned This is necessary because we are not simplyoutputting an inline function call: Some container must be there for the result that is external
to the tag-based function invocation code
CFINVOKEARGUMENTalso enables you to conditionally include or modify arguments, because
it is a tag-based construction, as the following example shows:
<cfinvoke method=”add2” returnvariable=”theSum”>
<cfinvokeargument name=”firstNumber” value=”7”>
You have yet another option for calling CFINVOKE The arguments to the function can bepassed as extra attributes to CFINVOKE, as follows:
<cfinvoke method=”SumArgs” returnvariable=”theSum” firstNumber=”7”
secondNumber=”3”>