Awesome
CFML Tag to Script Conversions
A collection of examples demonstrating the conversion of CFML code blocks written in tags to CFScript.
Currently a work in progress!
This is not the CFScript syntax documentation you're (possibly) looking for. That awesome piece of work, by Adam Cameron, can be found here and is highly recommended as a reference to the content you'll find here.
If you happen to be searching for a resource to learn CFML, check out CFML in 100 Minutes by Mike Henke & Learn CF in a Week by this solid bunch of devs.
The examples in this document are an attempt to demonstrate conversions of CFML tag-based code to script-based code as a way of learning / migrating to CFScript. It is assumed that you already have a relatively firm understanding of the ColdFusion Markup Language (at least on a tag-based level). For the sake of modernity, the converted examples will be written to comply (from an agnostic approach) with the most current versions of the various CFML engines at the time of writing - (ColdFusion 11, Railo 4.2 & Lucee 4.5).
Table of Contents
- The Modern Implementation of Tags to Script
- Comments
- Variables
- Operators
- Conditions
- Iterations
- Other Flow Control Tags
- General
- Debugging
- Database
- File System Operations
- Image Manipulation
- Spreadsheet Integration
- Tags Implemented as Components
- Interfaces, Components & Functions
- Real World Conversions By Example
- Tags Have Their Place
The <em>Modern</em> Implementation of Tags to Script
I want to open with this before diving into the depths of conversions. Adobe ColdFusion 11+, Railo 3.2+(?) and Lucee 4.5+ all offer some kind of full script syntax support (Railo & Lucee sharing the implementation) involving <em>near</em> identical syntax between tag-base and script-base. Adobe ColdFusion 11 rolled it's own variation but Railo and Lucee both support ACF's syntax as of versions 4.2 and 4.5 respectively.
As per Adam Cameron's CFScript Documentation:
On Railo/Lucee this is a matter of removing the "
<cf
" and the ">
", and using normal block syntax (curly braces) where the tag-version is a block-oriented tag.
On ColdFusion, replace the "
<cftagname
" with "cftagname(
", and the ">
" with ")
", and comma-separate the attributes. Note that this will make the construct look like a function, but it actually is not, and cannot be used like a function, eg this is invalid syntax:
result = cfhttp(method="post", url="http://example.com");
Where the correct way would be: cfhttp(result="result", method="post", url="http://example.com");
Comments
Tags:
<!--- I'm a single-line comment. --->
<!---
I'm a multi-
line comment.
--->
Script:
<cfscript>
// I'm a single-line comment.
// Notice that CFScript code within a ".cfm" file must be wrapped in <cfscript> tags.
/*
I'm a multi-
line comment.
*/
</cfscript>
Variables
Tags:
<!--- Some simple variable statements in tags --->
<!--- Default variable declarations --->
<cfparam name="title" default="">
<cfparam name="views" type="numeric" default=0>
<!--- Regular assignment --->
<cfset title = "Tags">
<cfset views = 1>
<cfset page = "Title: #title#, Number: #views#">
Script:
<cfscript>
// Some simple variable statements in script
// Default variable declarations
param name="title" default="";
param name="views" type="numeric" default=0;
// Regular assignment
title = "Script";
views = 2;
page = "Title: #title#, Number: #views#";
// or
page = "Title: " & title & ", " & "Number: " & views;
</cfscript>
Operators
Decision
Tags:
<cfset x = 1>
<cfset y = [
x EQ 1,
x NEQ 1,
x LT 1,
x GT 1,
x LTE 1,
x GTE 1
]>
<cfdump var="#y#">
Script:
<cfscript>
// All tag based operators still work in script
// There are also these equivalents
x = 1;
y = [
x == 1,
x != 1,
x < 1,
x > 1,
x <= 1,
x >= 1
];
writeDump(y);
</cfscript>
Increment / Decrement
Tags:
<!--- Increment --->
<cfset x = 1>
<cfset y = x++>
<cfset z = ++x> <!--- or z = x + 1 --->
<cfdump var="#[x, y, z]#">
<!--- Decrement --->
<cfset x = 1>
<cfset a = x-->
<cfset b = --x> <!--- or b = x - 1 --->
<cfdump var="#[a, b]#">
Script:
<cfscript>
// Increment
x = 1;
y = x++;
z = ++x // or z = x + 1
writeDump([x, y, z]);
// Decrement
a = x--;
b = --x // or b = x - 1
writeDump([a, b]);
</cfscript>
Inline Assignment
Tags:
<cfset x = 0>
<cfset y = "string">
<cfset x += 10>
<!--- or x = x + 10 --->
<cfdump var="#x#">
<cfset x -= 8>
<!--- or x = x - 8 --->
<cfdump var="#x#">
<cfset x /= 6>
<!--- or x = x / 6 --->
<cfdump var="#x#">
<cfset x *= 4>
<!--- or x = x * 4 --->
<cfdump var="#x#">
<cfset x %= 2>
<!--- or x = x MOD 2 or x = x % 2 --->
<cfdump var="#x#">
<cfset y &= "s">
<!--- or y = y & "s" --->
<cfdump var="#y#">
Script:
<cfscript>
x = 0;
y = "string";
x += 10;
// or x = x + 10
writeDump(x);
x -= 8;
// or x = x - 8
writeDump(x);
x /= 6;
// or x = x / 6
writeDump(x);
x *= 4;
// or x = x * 4
writeDump(x);
x %= 2;
// or x = x MOD 2 or x = x % 2
writeDump(x);
y &= "s";
// or y = y & "s"
writeDump(x);
</cfscript>
Boolean
Tags:
<cfset x = y = 1>
<cfset z = [
NOT x,
x EQ 1 AND y EQ 2,
x EQ 1 OR y EQ 2
]>
<cfdump var="#z#">
Script:
<cfscript>
x = y = 1;
z = [
!x,
x == 1 && y == 2,
x == 1 || y == 2
];
writeDump(z);
</cfscript>
Ternary & Null-Coalescing
Tags:
<!--- Ternary --->
<cfset x = "Tags">
<cfset y = len(x) ? x : "something else">
<cfdump var="#y#">
<!--- Null-Coalescing --->
<cfset y = z ?: "something else">
<cfdump var="#y#">
Script:
<cfscript>
// Ternary
x = "CFScript";
y = len(x) ? x : "something else";
writeDump(y);
// Null-Coalescing
y = z ?: "something else";
writeDump(y);
</cfscript>
Conditions
cfif / cfelseif / cfelse
Tags:
<cfset count = 10>
<cfif count GT 20>
<cfoutput>#count#</cfoutput>
<cfelseif count EQ 8>
<cfoutput>#count#</cfoutput>
<cfelse>
<cfoutput>#count#</cfoutput>
</cfif>
Script:
<cfscript>
count = 10;
if (count > 20) {
writeOutput(count);
} else if (count == 8) {
writeOutput(count);
} else {
writeOutput(count);
}
</cfscript>
cfswitch
Tags:
<cfset fruit = "">
<cfswitch expression="#fruit#">
<cfcase value="Apple">
<cfoutput>I like apples!</cfoutput>
</cfcase>
<cfcase value="Orange">
<cfoutput>I like oranges!</cfoutput>
</cfcase>
<cfcase value="Kiwi">
<cfoutput>I like kiwi!</cfoutput>
</cfcase>
<cfdefaultcase>
<cfoutput>Fruit, what fruit?</cfoutput>
</cfdefaultcase>
</cfswitch>
Script:
<cfscript>
fruit = "";
switch(fruit) {
case "Apple":
writeOutput("I like apples!");
break;
case "Orange":
writeOutput("I like oranges!");
break;
case "Kiwi":
writeOutput("I like kiwi!");
break;
default:
writeOutput("Fruit, what fruit?");
break;
}
</cfscript>
cftry / cfcatch / cffinally
Tags:
<cftry>
<cfset x = y + z>
<cfcatch type="any">
<cfoutput>#cfcatch.message#</cfoutput>
</cfcatch>
<cffinally>
<cfoutput>Finally at the end.</cfoutput>
</cffinally>
</cftry>
Script:
<cfscript>
try {
x = y + z;
}
catch(any e) {
writeOutput(e.message);
}
finally {
writeOutput("Finally at the end");
}
</cfscript>
cfthrow & cfrethrow
Tags:
<!--- <cfthrow> --->
<cfthrow message="Oh no an error!!1" type="RealBadErrorException" detail="Something bad happened in detail">
<!--- <cfrethrow> --->
<cfrethrow>
Script:
<cfscript>
// <cfthrow>
throw(message = "Oh no an error!!1", type = "RealBadErrorException", detail = "Something bad happened in detail");
// throw() can even be as simple as this...
throw "Oh no an error!!1";
// <cfrethrow>
rethrow;
</cfscript>
Iterations
General Purpose Loop
Tags:
<cfloop index="i" from="1" to="10">
<cfoutput>#i#</cfoutput>
</cfloop>
Script:
<cfscript>
for (i = 1; i <= 10; i++) {
writeOutput(i);
}
</cfscript>
Array Loop
Tags:
<!--- Define our array --->
<cfset myArray = ["a", "b", "c"]>
<!--- By index --->
<cfloop index="i" from="1" to="#arrayLen(myArray)#">
<cfoutput>#myArray[i]#</cfoutput>
</cfloop>
<!--- By array --->
<cfloop index="currentIndex" array="#myArray#">
<cfoutput>#currentIndex#</cfoutput>
</cfloop>
Script:
<cfscript>
// Define our array
myArray = ["a", "b", "c"];
// By index
// Note the use of the newer member function syntax for arrayLen()
for (i = 1; i <= myArray.len(); i++) {
writeOutput(myArray[i]);
}
// By array
for (currentIndex in myArray) {
writeOutput(currentIndex);
}
// By arrayEach()
myArray.each(function(element, index) {
writeOuput(element & " : " & index);
});
</cfscript>
Struct Loop
Tags:
<!--- Define our struct --->
<cfset myStruct = {name: "Tony", state: "Florida"}>
<!--- By struct --->
<cfloop item="currentKey" collection="#myStruct#">
<cfoutput><li>#currentKey# : #myStruct[currentKey]#</li></cfoutput>
</cfloop>
Script:
<cfscript>
// Define our struct
myStruct = {name: "Tony", state: "Florida"};
// By struct
for (currentKey in myStruct) {
writeOutput("<li>#currentKey# : #myStruct[currentKey]#</li>");
}
// By structEach()
myStruct.each(function(key, value) {
writeOutput("<li>#key# : #value#</li>");
});
</cfscript>
List Loop
Tags:
<!--- Define our list --->
<cfset myList = "a, b, c">
<!--- By list --->
<cfloop index="item" list="#myList#">
<cfoutput>#item#</cfoutput>
</cfloop>
<!--- By array --->
<cfloop index="currentIndex" array="#listToArray(myList, ",")#">
<cfoutput>#currentIndex#</cfoutput>
</cfloop>
Script:
<cfscript>
// Define our list
myList = "a, b, c";
// By array
for (item in listToArray(myList, ",")) {
writeOutput(item);
}
// By listEach()
myList.each(function(element, index) {
writeOuput(element & " : " & index);
}, ",");
</cfscript>
Query Loop
Tags:
<!--- Define our query --->
<cfset platform = ["Adobe ColdFusion", "Railo", "Lucee"]>
<cfset myQuery = queryNew("")>
<cfset queryAddColumn(myQuery, "platform", "CF_SQL_VARCHAR", platform)>
<!--- By row index --->
<cfloop index="i" from="1" to="#myQuery.recordCount#">
<cfoutput><li>#myQuery["platform"][i]#</li></cfoutput>
</cfloop>
<!--- By group --->
<cfloop query="myQuery" group="platform">
<cfoutput><li>#platform#</li></cfoutput>
</cfloop>
Script:
<cfscript>
// Define our query
platform = ["Adobe ColdFusion", "Railo", "Lucee"];
myQuery = queryNew("");
queryAddColumn(myQuery, "platform", "CF_SQL_VARCHAR", platform);
// By row index
for (i = 1; i <= myQuery.recordCount; i++) {
writeOutput("<li>#myQuery["platform"][i]#</li>");
}
// By query
for (row in myQuery) {
writeOutput("<li>#row.platform#</li>");
}
</cfscript>
Other Flow Control Tags
cfabort & cfexit
Tags:
<!--- <cfabort> --->
<cfabort statusError="My error message">
<!--- <cfexit> --->
<cfexit method="method">
General
cfoutput
Tags:
<cfoutput>Some text and a #variable#</cfoutput>
Script:
<cfscript>
writeOutput("Some text and a #variable#");
// or
writeOutput("Some text and a " & variable);
</cfscript>
cfsavecontent
Tags:
<cfsavecontent variable="myContent">
<cfoutput>Some content.</cfoutput>
</cfsavecontent>
Script:
<cfscript>
savecontent variable="myContent" {
writeOutput("Some content.");
}
</cfscript>
cfthread
Tags:
<cfthread action="run" name="myThread">
<!--- Do single thread stuff --->
</cfthread>
<cfthread action="join" name="myThread,myOtherThread" />
Script:
<cfscript>
thread action="run" name="myThread" {
// do single thread stuff
}
thread action="join" name="myThread,myOtherThread";
</cfscript>
cflock
Tags:
<cflock timeout="60" scope="session" type="exclusive">
<cfset session.myVar = "Hello">
</cflock>
Script:
<cfscript>
lock timeout="60" scope="session" type="exclusive" {
session.myVar = "Hello";
}
</cfscript>
cfinclude
Tags:
<cfinclude template="mypage.cfm">
Script:
<cfscript>
include "mypage.cfm";
</cfscript>
cflocation
Tags:
<cflocation url="mypage.cfm" addToken="false" statusCode="301">
Script:
<cfscript>
location("mypage.cfm", "false", "301");
</cfscript>
Debugging
cfdump
Tags:
<cfdump var="#cgi#" label="CGI Scope">
Script:
<cfscript>
writeDump(var = cgi, label = "CGI Scope");
</cfscript>
cflog
Tags:
<cflog text="Logging some info." type="information" application="no" file="myLogFile">
Script:
<cfscript>
writeLog(text = "Logging some info.", type = "information", application = "no", file = "myLogFile");
</cfscript>
Script:
<cfscript>
// <cfabort>
abort "My error message";
// <cfexit>
exit "method";
</cfscript>
Database
cfquery
Tags:
<cfquery name="myQuery" datasource="myDSN">
SELECT myCol1, myCol2 FROM myTable
WHERE myCol1 = <cfqueryparam value="#myId#" cfsqlype="cf_sql_integer">
ORDER BY myCol1 ASC
</cfquery>
Script:
<cfscript>
// Also see the "Tags Implemented as Components" section for another method of using <cfquery> in script
myQuery = queryExecute(
"SELECT myCol1, myCol2 FROM myTable
WHERE myCol1 = :myId
ORDER BY myCol1 ASC",
{myId: {value: 5, cfsqltype: "cf_sql_integer"}},
{datasource = "myDSN"}
);
</cfscript>
cftransaction
Tags:
<cftransaction>
<cftry>
<!--- code to run --->
<cftransaction action="commit" />
<cfcatch type="any">
<cftransaction action="rollback" />
</cfcatch>
</cftry>
</cftransaction>
Script:
<cfscript>
transaction {
try {
// code to run
transaction action="commit";
}
catch(any e) {
transaction action="rollback";
}
}
</cfscript>
File System Operations
cfdirectory
Tags:
<!--- Directory List --->
<cfdirectory action="list" directory="#expandPath("./")#" recurse="false" name="myList">
<!--- Directory Create --->
<cfdirectory action="create" directory="#expandPath("./new_directory")#">
<!--- Directory Delete --->
<cfdirectory action="delete" directory="#expandPath("./my_directory")#">
<!--- Directory Rename --->
<cfdirectory action="rename" directory="#expandPath("./my_directory")#" newdirectory="#expandPath("./new_directory")#">
Script:
<cfscript>
// Directory List
myList = directoryList(expandPath("./"), false, "query");
// Directory Create
directoryCreate(expandPath("./new_directory"));
// Directory Delete
directoryDelete(expandPath("./my_directory"));
// Directory Rename
directoryRename(expandPath("./my_directory"), expandPath("./new_driectory"));
</cfscript>
cffile
Tags:
<!--- File Write --->
<cffile action="write" file="#expandPath("./myFile.txt")#" output="Here's some content for my file.">
<!--- File Append --->
<cffile action="append" file="#expandPath("./myFile.txt")#" attributes="normal" output="Here's some new content.">
<!--- File Read --->
<cffile action="read" file="#expandPath("./myFile.txt")#" variable="myFile">
<!--- File Read Binary --->
<cffile action="readBinary" file="#expandPath("./myImage.jpg")#" variable="myImageBinary">
<!--- File Rename --->
<cffile action="rename" source="#expandPath("./myFile.txt")#" destination="#expandPath("./myNewFileName.txt")#" attributes="normal">
<!--- File Copy --->
<cffile action="copy" source="#expandPath("./myFile.txt")#" destination="#expandPath("./some/other/path")#">
<!--- File Move --->
<cffile action="move" source="#expandPath("./myFile.txt")#" destination="#expandPath("./some/other/path")#">
<!--- File Delete --->
<cffile action="delete" file="#expandPath("./myFile.txt")#">
<!--- File Upload --->
<cffile action="upload" destination="#expandPath("./destination)#" filefield="form.myFile" nameconflict="makeunique">
<!--- File Upload All --->
<cffile action="uploadall" destination="#expandPath("./destination")#" nameconflict="makeunique">
Script:
<cfscript>
// File Write
fileWrite(expandPath("./myFile.txt"), "Here's some content for my file.");
// File Append
// There is no "fileAppend()" so we access the file and use fileWriteLine()
myFile = fileOpen(expandPath("./myFile.txt"), "write");
fileWriteLine(myFile, "Here's some new content.");
fileClose(myFile);
// File Read
myFile = fileRead(expandPath("./myFile.txt"));
// File Read Binary
myImageBinary = fileReadBinary(expandPath("./myImage.jpg"));
// File Rename
// Since there is no "fileRename()", fileMove() works just as well
fileMove(expandPath("./myFile.txt"), expandPath("./myNewFileName.txt"));
// File Copy
fileCopy(expandPath("./myFile.txt"), expandPath("./some/other/path"));
// File Move
fileMove(expandPath("./myFile.txt"), expandPath("./some/other/path"));
// File Delete
fileDelete(expandPath("./myFile.txt"));
// File Upload
fileUpload(expandPath("./destination"), form.myFile, "", "makeunique");
// File Upload All
fileUploadAll(expandPath("./destination"), "", "makeunique");
</cfscript>
Image Manipulation
cfimage
There is <em>quite</em> the lot of image-related functions for <cfimage> that are already decently documented by Adobe. For the sake of avoiding a lengthy bit of repeating, you can check out those functions here - ColdFusion Wiki: Image Functions
Spreadsheet Integration
cfspreadsheet
Much like <cfimage>, <cfspreadsheet> has a fair collection of functions for working with spreadsheets which you can find here - ColdFusion Wiki: Microsoft Office Integration Functions
Tags Implemented as Components
Note: If you are using ColdFusion 11+, (highly) consider using the script versions over the CFC implementations for example:
cfhttp(result="result", method="post", url="http://mydomain.com");
overHttp.cfc
andqueryExecute()
overQuery.cfc
.
cfquery / query.cfc
Tags:
<cfquery name="myQuery" datasource="myDSN">
SELECT myCol1, myCol2 FROM myTable
WHERE myCol1=<cfqueryparam value="#myId#" cfsqlype="cf_sql_integer">
ORDER BY myCol1 ASC
</cfquery>
Script:
<cfscript>
myQuery = new Query().setDatasource("myDSN");
myQuery.setSQL("
SELECT myCol1, myCol2 FROM myTable
WHERE myCol1=:myId
ORDER BY myCol1 ASC
");
myQuery.addParam(name: "myCol1", value: "#myId#", cfsqltype: "cf_sql_integer");
myQuery = myQuery.execute().getResult();
</cfscript>
cfhttp / http.cfc
Tags:
<cfhttp result="result" method="GET" charset="utf-8" url="https://www.google.com/">
<cfhttpparam name="q" type="formfield" value="cfml">
</cfhttp>
<cfdump var="#result#">
Script:
<cfscript>
httpService = new http(method = "GET", charset = "utf-8", url = "https://www.google.com/");
httpService.addParam(name = "q", type = "formfield", value = "cfml");
result = httpService.send().getPrefix();
writeDump(result);
</cfscript>
cfmail / mail.cfc
Tags:
<cfmail to="your@email.com" from="another@email.com" subject="CFMail Example">
Your Email Message!!1
</cfmail>
Script:
<cfscript>
// It's a good idea to build your message inside a save content block first
savecontent variable="mailBody" {
writeOutput("Your Email Message!!1");
};
// Create and populate the mail object
mailService = new mail(
to = "your@email.com",
from = "another@email.com",
subject = "CFMail Example",
body = mailBody
);
// Send
mailService.send();
</cfscript>
cffeed / feed.cfc
Tags:
<!--- Read --->
<cffeed
name="feed"
action="read"
source="http://feeds.feedburner.com/ColdfusionbloggersorgFeed?format=xml"
query="feedQuery"
properties="feedMetadata"
>
<cfdump var="#feed#">
Script:
<cfscript>
// Read
feedService = new feed();
feed = feedService.read(
source = "http://feeds.feedburner.com/ColdfusionbloggersorgFeed?format=xml",
query = "feedQuery",
properties = "feedMetadata"
);
writeDump(feed);
</cfscript>
cfftp / ftp.cfc
Tags:
<!--- Open connection --->
<cfftp action="open" connection="myConn" username="myUName" password="myPW" server="ftp.server.com" stopOnError="true">
<!--- Get list of dir --->
<cfftp action="listdir" connection="myConn" name="filesList" directory="/" stopOnError="true">
<!--- Close connection --->
<cfftp action="close" connection="myConn" stopOnError="true">
Script:
<cfscript>
// Create FTP service and set attributes for connection
ftpService = new ftp();
ftpService.setConnection("myConn");
ftpService.setUsername("myUName");
ftpService.setPassword("myPW");
ftpService.setServer("ftp.server.com");
ftpService.setStopOnError(true);
// Open connection
ftpService.open();
// Get list of dir
fileList = ftpService.listdir(directory = "/", stopOnError = true).getResult();
// Close connection
ftpService.close();
</cfscript>
Interfaces, Components & Functions
cfinterface
Tags:
<cfinterface displayname="My Interface">
<cfunction name="myFunction" returntype="numeric">
<cfargument required="true" type="string" name="myArgument">
</cffunction>
</cfinterface>
Script:
interface displayname="My Interface" {
numeric function myFunction(required string myArgument);
}
cfcomponent
Tags:
<cfcomponent displayname="myComponent" output="false">
<!--- functions and other values here --->
</cfcomponent>
Script:
component displayname="myComponent" output="false" {
// functions and other values here
}
cffunction
Tags:
<cffunction access="public" returntype="boolean" name="myFunction">
<cfargument required="true" type="any" name="myArgument">
<!--- Some function bits --->
<cfreturn true>
</cffunction>
Script:
public boolean function myFunction(required any myArgument) {
// Some function bits
return true;
}
A More Complete Component Example
Tags:
<cfcomponent displayname="Utils" output="false">
<cfproperty name="version" type="string" default="0.0.1">
<cffunction access="public" returntype="string" name="doReverse" hint="I reverse the supplied string">
<cfargument required="true" type="string" name="stringToReverse">
<cfreturn reverse(arguments.stringToReverse)>
</cffunction>
<cffunction access="public" returntype="array" name="reverseArrayOrder">
<cfargument type="array" name="arrayToReverse" default="#["Adobe ColdFusion", "Lucee", "Railo"]#" hint="I reverse an array's order">
<cfset var result = []>
<cfset var i = 0>
<cfloop index="i" from="#arrayLen(arguments.arrayToReverse)#" to="1" step="-1">
<cfset arrayAppend(result, arguments.arrayToReverse[i])>
</cfloop>
<cfreturn result>
</cffunction>
</cfcomponent>
Script:
component displayname="Utils" output="false" {
property name="version" type="string" default="0.0.1";
public string function doReverse(required string stringToReverse)
hint="I reverse the supplied string"
{
return reverse(arguments.stringToReverse);
}
public array function reverseArrayOrder(array arrayToReverse = ["Adobe ColdFusion", "Lucee", "Railo"])
hint="I reverse an array's order"
{
var result = [];
for (var i = arrayLen(arguments.arrayToReverse); i >= 1; i--) {
result.append(arguments.arrayToReverse[i]);
}
return result;
}
}
Annotations
Information about a script-based component, property or function - it's attributes & arguments can be defined via the annotation syntax.
Script:
// Consider this code...
public numeric function adder(required numeric x, required numeric y)
hint="I add things"
{
return arguments.x + arguments.y;
}
// Now with annotations
/**
* @returnType numeric
* @hint I add things
*/
public function adder(required numeric x, required numeric y) {
return arguments.x + arguments.y;
}
Function Expressions
Consider the tag-based version of the function below. In CFScript, we can instead define a variable with an anonymous function that does the same task.
Tags:
<cffunction access="public" returntype="numeric" name="adder">
<cfargument required="true" type="numeric" name="x">
<cfargument required="true" type="numeric" name="y">
<cfreturn arguments.x + arguments.y>
</cffunction>
<cfoutput>#adder(2, 2)#</cfoutput>
Script:
<cfscript>
adder = function(x, y) {
return x + y;
};
writeOutput(adder(2, 2));
// You can be more specific with type, scope, required etc.
adder = function(required numeric x, required numeric y) {
return arguments.x + arguments.y;
};
writeOutput(adder(2, "a")); // this errors
</cfscript>
Real World Conversions By Example
The below examples are pieces of code found from online/offline sources. Anything with a name on it will be given credit and/or linked accordingly. If you see your code as an example and you don't want it here, LET ME KNOW! Cheers. - @cfchef
Example 1
Here's a function, written in tags, picked out from CFLib.org - utcOffsetToMinutes() by Mosh Teitelbaum
Tags:
<!---
Converts UTC Offset to minutes.
@param offset The formatted UTC offset to be converted to minutes. (Required)
@return Returns a number.
@author Mosh Teitelbaum (mosh.teitelbaum@evoch.com)
@version 1, 1/8/2015
--->
<cffunction name="utcOffsetToMinutes" returntype="numeric" output="no" description="Converts UTC Offset to minutes.">
<cfargument name="offset" type="string" required="yes" hint="The formatted UTC offset to be converted to minutes.">
<!--- Initialize local variables --->
<cfset var h = 0> <!--- hours --->
<cfset var m = 0> <!--- minutes --->
<cfset var s = 1> <!--- sign --->
<cfset var str = ReReplaceNoCase(arguments.offset, "[^-:0-9]", "", "ALL")> <!--- offset with non-important characters removed --->
<!--- If first character is "-", adjust sign --->
<cfif Compare(Left(str, 1), "-") EQ 0>
<cfset s = -1>
<cfset str = Right(str, Len(str) - 1)>
</cfif>
<!--- Determine number of hours and minutes --->
<cfif ListLen(str, ":") EQ 2>
<cfset h = ListFirst(str, ":")>
<cfset m = ListLast(str, ":")>
<cfelseif Len(str) EQ 2>
<cfset h = str>
<cfelseif Len(str) EQ 3>
<cfset h = Left(str, 1)>
<cfset m = Right(str, 2)>
<cfelseif Len(str) EQ 4>
<cfset h = Left(str, 2)>
<cfset m = Right(str, 2)>
</cfif>
<!--- Return number of minutes --->
<cfreturn s * ((h * 60) + m)>
</cffunction>
Script:
<cfscript>
/**
* @hint Converts UTC Offset to minutes.
* @param offset The formatted UTC offset to be converted to minutes. (Required)
* @return Returns a number.
* @author Mosh Teitelbaum (mosh.teitelbaum@evoch.com)
* @version 1, 1/8/2015
*/
public numeric function utcOffsetToMinutes(required string offset) {
// Initialize local variables
var h = 0; // hours
var m = 0; // minutes
var s = 1; // sign
var str = reReplaceNoCase(arguments.offset, "[^-:0-9]", "", "ALL"); // offset with non-important characters removed
// If first character is "-", adjust sign
if (compare(left(str, 1), "-") == 0) {
s = -1;
str = right(str, len(str) - 1);
}
// Determine number of hours and minutes
if (listLen(str, ":") == 2) {
h = listFirst(str, ":");
m = listLast(str, ":");
} else if (len(str) == 2) {
h = str;
} else if (len(str) == 3) {
h = left(str, 1);
m = right(str, 2);
} else if (len(str) == 4) {
h = left(str, 2);
m = right(str, 2);
}
// Return number of minutes
return s * ((h * 60) + m);
}
</cfscript>
Example 2
Here's another one from CFLib.org - addRemoveDebuggingIPAddress() by Qasim Rasheed
Tags:
<!---
This function will either add or remove an IP address to the list of debugging ip addresses if you do not have an administrator access.
@param ipAddress IP Address (Required)
@param action Add or Remove. Defaults to Add. (Optional)
@return Returns a list of IP addresses.
@author Qasim Rasheed (qasim_1976@yahoo.com)
@version 1, February 17, 2004
--->
<cffunction name="addRemoveDebuggingIPAddress" output="false" returnType="string">
<cfargument name="IPaddress" type="string" required="Yes" />
<cfargument name="action" type="string" default="Add" />
<cfscript>
var factory = CreateObject("Java","coldfusion.server.ServiceFactory");
var debuggingService = "";
</cfscript>
<cflock name="factory_debuggingservice" type="exclusive" timeout="5">
<cfset debuggingService = factory.getDebuggingService()>
<cfswitch expression="#arguments.action#">
<cfcase value="Add">
<cfif not listcontainsnocase(debuggingService.iplist.ipList,arguments.IPaddress)>
<cfset debuggingService.iplist.ipList = ListAppend(debuggingService.iplist.ipList,arguments.IPaddress)>
</cfif>
</cfcase>
<cfcase value="Remove">
<cfif listcontainsnocase(debuggingService.iplist.ipList,arguments.IPaddress)>
<cfset debuggingService.iplist.ipList = ListDeleteAt(debuggingService.iplist.ipList,ListFindNoCase(debuggingService.iplist.ipList,arguments.IPaddress))>
</cfif>
</cfcase>
</cfswitch>
<cfreturn debuggingService.iplist.ipList />
</cflock>
</cffunction>
Script:
<cfscript>
/**
* @hint This function will either add or remove an IP address to the list of debugging ip addresses if you do not have an administrator access.
* @param ipAddress IP Address (Required)
* @param action Add or Remove. Defaults to Add. (Optional)
* @return Returns a list of IP addresses.
* @author Qasim Rasheed (qasim_1976@yahoo.com)
* @version 1, February 17, 2004
*/
public string function addRemoveDebuggingIPAddress(required string IPaddress, string action = "Add")
output="false"
{
var factory = createObject("java","coldfusion.server.ServiceFactory");
var debuggingService = "";
lock name="factory_debuggingservice" type="exclusive" timeout="5" {
debuggingService = factory.getDebuggingService();
switch(arguments.action) {
case "Add":
if (!listContainsNoCase(debuggingService.iplist.ipList,arguments.IPaddress)) {
debuggingService.iplist.ipList = listAppend(debuggingService.iplist.ipList, arguments.IPaddress);
}
break;
case "Remove":
if (listContainsNoCase(debuggingService.iplist.ipList, arguments.IPaddress)) {
debuggingService.iplist.ipList = listDeleteAt(debuggingService.iplist.ipList, listFindNoCase(debuggingService.iplist.ipList, arguments.IPaddress));
}
break;
}
return debuggingService.iplist.ipList;
}
}
</cfscript>
Example 3
Yet another example from CFLib.org - collectFiles() by Steven Ross
Tags:
<!---
Scans a directory (or path) for files of a specified extension and then copies them to the path you specify.
v2 by Raymond Camden. I just cleaned up the var statements.
@param extensions List of extensions to copy. (Required)
@param destinationPath Destination directory. (Required)
@param sourcePath Source directory. (Required)
@return Returns nothing.
@author Steven Ross (steven.ross@zerium.com)
@version 2, April 7, 2006
--->
<cffunction name="collectFiles" access="public" hint="recurses through a directory and collects the file types you want then outputs to another directory" returnType="void">
<cfargument name="extensions" required="true" type="string" hint="The extensions you want to gather up csv (list) format ex:(asp,cfm,jsp) ">
<cfargument name="destinationPath" required="true" type="string" hint="absolute path to storage directory">
<cfargument name="sourcePath" required="true" type="string" hint="absolute path to source directory">
<cfset var root = arguments.sourcePath/>
<cfset var i = "">
<cfset var absPath = "">
<cfset var relativePath = "">
<cfset var writeTo = "">
<cfset var pathAndFile = "">
<cfif not directoryExists(arguments.sourcePath)>
<cfthrow message="Source Directory (#arguments.sourcePath#) not found" detail="You didn't pass in a valid source directory, check the path and try again.">
</cfif>
<cfloop list="#arguments.extensions#" index="i">
<cfdirectory name="getFiles" directory="#root#" recurse="true" filter="*.#i#">
<cfloop query="getFiles">
<cfset absPath = getFiles.directory & "/" />
<cfset relativePath = Replace(absPath, root, "", "all") />
<cfset writeTo = ARGUMENTS.destinationPath & "/" & relativePath>
<cfset pathAndFile = getFiles.directory & "/" & getFiles.name />
<cfif not directoryExists(writeTo)>
<cfdirectory action="create" directory="#writeTo#">
<cffile action="copy" source="#pathAndFile#" destination="#writeTo#">
<cfelse>
<cffile action="copy" source="#pathAndFile#" destination="#writeTo#">
</cfif>
</cfloop>
</cfloop>
</cffunction>
Script:
<cfscript>
/**
* @hint Scans a directory (or path) for files of a specified extension and then copies them to the path you specify.
* @param extensions List of extensions to copy. (Required)
* @param destinationPath Destination directory. (Required)
* @param sourcePath Source directory. (Required)
* @return Returns nothing.
* @author Steven Ross (steven.ross@zerium.com) - v2 by Raymond Camden. I just cleaned up the var statements.
* @version 2, April 7, 2006
*/
public void function collectFiles(required string extensions, required string destinationPath, required string sourcePath) {
var root = arguments.sourcePath;
if (!directoryExists(arguments.sourcePath)) {
throw(
message = "Source Directory (#arguments.sourcePath#) not found",
detail = "You didn't pass in a valid source directory, check the path and try again."
);
}
for (var ext in listToArray(arguments.extensions)) {
var list = directoryList(path = root, recurse = true, listinfo = "query", filter = "*.#ext#");
for (var dir in list) {
var absPath = dir.directory & "/";
var relativePath = replace(absPath, root, "", "all");
var writeTo = arguments.destinationPath & "/" & relativePath;
var pathAndFile = dir.directory & "/" & dir.name;
if (!directoryExists(writeTo)) {
directoryCreate(writeTo);
fileCopy(pathAndFile, writeTo);
} else {
fileCopy(pathAndFile, writeTo);
}
}
}
}
</cfscript>
Tags Have Their Place
Keeping Tags in the View
The modern CFML world has evolved with various flavors and mixes of Model View Controller (MVC) & Object Oriented Programming (OOP) practices. While procedural coding might have a place, larger applications often call for a more organized, structured collection of functions and how and where they display their results. As opposed to being <em>all</em> in one file, database calls should reside in their own file (Model), structuring/manipulation of that data can be built in a seperate file based on user supplied variables (Controller) and then that structured data is formatted with HTML for display (View).
From a general standpoint, the view layer should be the home for basic tag functions to help iterate and organize received result data; to be married with your HTML and displayed to the user. Simple tags such as
<cfset>
,<cfparam>
,<cfoutput>
,<cfif>
&<cfloop>
. With the involvement of HTML in the view, tags make sense as they work with the flow of the layout; leaving your Model and Controllers to be uncluttered and have a clean flow of their own in CFScript. It might be a matter of opinion but you will eventually grow to find this separation quite refreshing visually and mentally when scanning through large blocks of logic vs display code.
Consider this general mock breakdown of code presenting blog posts to the user. This would normally be handled more effectively & efficiently in a MVC framework like FW/1 or Coldbox.
Model: BlogService.cfc
/**
* @hint I contain blog related utilities
*/
component displayname="Blog Service"
output="false"
{
/**
* @hint I intiate the service object by setting the datasource
*/
public BlogService function init(required string dsn) {
variables.dsn = arguments.dsn;
return this;
}
/**
* @hint I return a query of blog posts
*/
public query function getPosts() {
var qry = new Query();
qry.setDatasource(variables.dsn);
qry.setSQL("
SELECT postId, postTitle, postBody, author, publishDate
FROM posts
ORDER BY postId DESC
");
return qry.execute().getResult();
}
}
Controller: BlogController.cfc
/**
* @hint I receive user request and translate them into response
*/
component displayname="Blog Controller"
output="false"
{
/**
* @hint I initiate the BlogController and call in all required objects
*/
public BlogController funtion init() {
var DataService = createObject("component", "model.services.DataService").init();
variables.BlogService = createObject("component", "model.services.BlogService").init(DataService.getDSN());
return this;
}
/**
* @hint I return a formatted collection of blog posts for display
*/
public struct function posts() {
// Define out request context struct for holding data to pass on to the view
var rc = {};
var postQry = variables.BlogService.getPosts();
// Let's make a friendlier collection as a array of structs of the data
rc.posts = [];
if (postQuery.recordCount) {
for (var post in postQry) {
arrayAppend(rc.posts, {
title: post.postTitle,
body: post.postBody,
author: post.author,
published: post.publishDate
});
}
}
return rc;
}
}
View: posts.cfm
<cfparam name="rc.posts" default="#[]#">
<cfset BlogController = createObject("component", "blog.controllers.BlogController").init()>
<cfset rc.posts = BlogController.posts()>
<h1>Welcome to my Blog!</h1>
<cfif arrayLen(rc.posts)>
<div class="post-content">
<cfoutput>
<cfloop index="post" array="#rc.posts#">
<h2>#post.title#</h2>
<div class="post-meta">Published on: #post.published# By: #post.author#</div>
<div class="post-body">#post.body#</div>
</cfloop>
</cfoutput>
</div>
<cfelse>
<h3>No posts to display!</h3>
</cfif>
Building Tag Code on the Script Side
While we generally want to keep our presentation layer separate from our business logic, as briefly portrayed in Keeping Tags in the View, there are some scenarios where we need to do string-building in a function, or similar, to later pass on for an alternate method of viewing in HTML, XML, email etc.
Consider these examples. . .
Email - Tags
<!--- Dummy blog post --->
<cfset post = {
title: "My New Blog Post!",
slug: "my-new-blog-post",
body: "Some body content for the post.",
publishDate: "{ts '2015-07-06 18:17:01'}"
}>
<!--- Build our email --->
<cfsavecontent variable="mailBody">
<cfoutput>
<h4>A New Article Has Been Posted @ myblog.com!</h4>
<h6>#post.title#</h6>
<p><b>Posted on:</b> #dateFormat(post.publishDate, "YYYY/MM/DD")#</p>
<p>
<cfif len(post.body) GT 500>
#left(post.body, 500)#. . .
<cfelse>
#post.body#
</cfif>
</p>
<a href="http://myblog.com/blog/#post.slug#"><b>Read more...</b></a>
</cfoutput>
</cfsavecontent>
<!--- Send email to subscribers --->
<cfloop item="subscriber" collection="#SubscriberService.getSubscribers()#">
<cfmail
type="html"
to="#subscriber.getEmail()#"
from="no-reply@myblog.com"
subject="New Blog Post from MyBlog.com"
body="#mailBody#"
>
</cfloop>
Email - Script
<cfscript>
// Dummy blog post
post = {
title: "My New Blog Post!",
slug: "my-new-blog-post",
body: "Some body content for the post.",
publishDate: "{ts '2015-07-06 18:17:01'}"
};
// Build our email
savecontent variable="mailBody" {
writeOutput('
<h4>A New Article Has Been Posted @ myblog.com!</h4>
<h6>#post.title#</h6>
<p><b>Posted on:</b> #dateFormat(post.publishDate, "YYYY/MM/DD")#</p>
<p>
if (len(post.body) > 500) {
#left(post.body, 500)#. . .
} else {
#post.body#
}
</p>
<a href="http://myblog.com/blog/#post.slug#"><b>Read more...</b></a>
');
};
// Send email to subscribers
for (subscriber in SubscriberService.getSubscribers()) {
mailService = new mail(
type = "html",
to = subscriber.getEmail(),
from = "no-reply@myblog.com",
subject = "New Blog Post from MyBlog.com!",
body = encodeForHTML(mailBody)
);
mailService.send();
}
</cfscript>
XML Sitemap - Tags
<!--- Dummy links --->
<cfset links = [
"http://myblog.com",
"http://myblog.com/about",
"http://myblog.com/blog",
"http://myblog.com/blog/my-blog-post"
]>
<cftry>
<!--- Build XML --->
<cfsavecontent variable="xmlData">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<cfloop index="link" array="#links#">
<url>
<loc>#link#</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</cfloop>
</urlset>
</cfoutput>
</cfsavecontent>
<!--- Write to file --->
<cffile action="write" file="#expandPath("./Sitemap.xml")#" output="#xmlData#">
<cfcatch type="any">
<cfoutput>#cfcatch.message#</cfoutput>
</cfcatch>
</cftry>
XML Sitemap - Script
<cfscript>
// Dummy links
links = [
"http://myblog.com",
"http://myblog.com/about",
"http://myblog.com/blog",
"http://myblog.com/blog/my-blog-post"
];
try {
// Build XML
savecontent variable="xmlData" {
writeOutput('
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
');
for (link in links) {
writeOutput('
<url>
<loc>#link#</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
');
}
writeOutput('
</urlset>
');
}
// Write to file
fileWrite(expandPath("./Sitemap.xml"), xmlData);
}
catch(any e) {
writeOutput(e.message);
}
</cfscript>
LICENSE
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">CFML Tag to Script Conversions</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://tonyjunkes.com/blog/leave-your-tags-at-the-door-some-cfml-tag-to-script-documentation" property="cc:attributionName" rel="cc:attributionURL">Tony Junkes</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.