Definition
A ColdFusion UDF (User Defined Function) is a custom reusable block of CFML code that you define to perform a specific task and call by name whenever you need it. In simple terms, a UDF is your own function—just like built-in functions—written in ColdFusion Markup Language (CFML) using either script Syntax (cfscript) or tag-based Syntax. UDFs help you avoid duplicating logic, keep code organized, and improve maintainability.
A UDF can:
- Accept input parameters (arguments)
- Perform calculations or manipulations
- Return a value (or sometimes perform an action)
- Live in a page, an include file, or a CFC (ColdFusion Component)
How It Works
A UDF is defined once and then invoked anywhere in its scope. ColdFusion compiles the function the first time it’s called on a request; subsequent calls reuse the compiled code.
Key concepts:
- Arguments: Inputs defined with a name, type, and optional default value.
- Return value: The result your function returns, often typed (string, numeric, boolean, array, struct, query, any, etc.).
- Scope: Where the function is visible (local page/request, included files, components, or application/shared scopes).
- Output: Whether the function is allowed to write to the response buffer (output=”false” is usually best).
Defining a UDF in CFScript
CFScript is the JavaScript-like syntax for CFML and is common for defining UDFs.
Example: A reusable slug generator
/**
- Convert a string into a URL-friendly slug.
*/
function slugify(required string input, boolean lowercase = true) return string output=false {
// Always use the local scope or var declarations for function-local variables
var s = trim(input);
// Replace non-alphanumeric with hyphens
s = reReplace(s, “[^A-Za-z0-9]+”, “-“, “all”);
// Remove leading/trailing hyphens
s = reReplace(s, “^-+|-+$”, “”, “all”);
if (lowercase) {
s = lCase(s);
}
return s;
}
// Usage
writeOutput( slugify(“ColdFusion: User Defined Functions!”) );
// -> “coldfusion-user-defined-functions”
Notes:
- required enforces that the argument must be passed.
- boolean lowercase = true sets a default value.
- return string and output=false are Best practices for clarity and Performance.
Defining a UDF in Tag-Based CFML
Some teams prefer classic tag syntax, especially when mixing with templates.
Notes:
- cfargument specifies types, Default values, and whether arguments are required.
- output=”false” prevents unwanted content from being written by the function.
Arguments, Defaults, and Validation
- Use required for essential inputs.
- Provide sensible defaults to make calling the function simpler.
- Use typed arguments (string, numeric, boolean, array, struct, query, any) for safer code.
- Validate data inside the function for business rules (e.g., throw an error if format is invalid).
Example:
function calcDiscount(required numeric price, numeric percent = 10) return numeric output=false {
if (price LT 0 OR percent LT 0) {
throw type=”ValidationException” message=”Price and percent must be non-negative.”;
}
return price * (percent / 100);
}
Return Values and Output
- Prefer returning a value (functional style) over writing output inside the function.
- Mark functions output=false to avoid mixing markup with logic and to improve Performance.
- If a function must generate content, keep it explicit and consider returning the content as a string for the caller to output.
Real-World Use Case: Normalizing and Validating Phone Numbers
Suppose your application receives phone numbers from multiple sources. You want to:
- Validate the format
- Normalize to a consistent E.164 form
- Use the same logic across pages, Scheduled tasks, and APIs
Utility UDF:
/**
- Normalize a US phone number to E.164 (+1XXXXXXXXXX).
*/
function normalizeUSPhone(required string input) return string output=false {
var digits = reReplace(input, “[^0-9]”, “”, “all”);
// Handle leading “1”
if (len(digits) EQ 11 AND left(digits,1) EQ “1”) {
digits = right(digits,10);
}
if (len(digits) NEQ 10) {
throw type=”ValidationException” message=”Phone must have 10 digits (US).”;
}
return “+1” & digits;
}
Usage in a handler or page:
try {
user.normalizedPhone = normalizeUSPhone(form.phone);
// Save to database…
} catch (any e) {
// Show a friendly validation error
writeOutput(“Invalid phone number: ” & e.message);
}
Benefits:
- One source of truth for validation rules
- Easy to unit test
- Reusable across controllers, services, and scheduled jobs
Where to Store and Reuse UDFs
- Single file include: Place related UDFs in /includes/udf_util.cfm and
them where needed. Simple but can get messy with scope if overused. - ColdFusion Component (CFC) method: Group functions as methods in a utility CFC, e.g., com.app.util.Strings.cfc. Call them via dependency injection (DI) or createObject. This is the preferred approach for larger apps.
- Application-wide scopes: Attach function references to application or server scopes for global reuse, but manage carefully to avoid tight coupling.
Example CFC:
component output=false hint=”String utilities” {
public string function slugify(required string input, boolean lowercase=true) output=false {
// implementation from above
}
}
Usage:
strings = new com.app.util.Strings();
slug = strings.slugify(“Hello CFML”);
Best practices
- Use output=false for UDFs
- Prefer pure functions: no side effects; return values instead of writing to output or changing global state
- Scope safety: use local scope (var in legacy CF) for all function variables to avoid leaking into broader scopes
- Type your arguments and return values: improves readability and catches errors early
- Use meaningful names and hints: document intent and usage
- Validate inputs and throw clear exceptions for invalid states
- Keep functions small and focused (single responsibility)
- Organize functions into logical CFCs for maintainability and testability
- Test your UDFs: use cfmunit, TestBox, or similar frameworks
- Avoid heavy database calls or remote services inside frequently called UDFs; consider caching when appropriate
Pros and cons of Using UDFs
Pros:
- Reusability and DRY code
- Better organization and readability
- Easier testing and Maintenance
- Encapsulation of business rules
- Type safety and argument validation
Cons:
- Over-fragmentation if you create too many tiny functions without structure
- Potential scope pitfalls if local/var is not used correctly
- Performance overhead if misused in tight loops without Optimization
- Confusion between UDFs and component methods when Architecture is unclear
Common Patterns and Advanced Features
Anonymous Functions and Closures
Modern ColdFusion supports anonymous functions (closures). Useful for callbacks with array operations.
Example:
numbers = [1,2,3,4,5];
squares = arrayMap(numbers, function(n){ return n*n; });
sum = arrayReduce(numbers, function(acc, n){ return acc + n; }, 0);
// squares -> [1,4,9,16,25]; sum -> 15
Closures capture outer-scope variables, enabling higher-order patterns while still encouraging pure, testable functions.
Argument Collections and Named Arguments
- Named arguments make calls clearer.
- ArgumentCollection lets you pass a struct of arguments dynamically.
function greet(required string name, string salutation=”Hello”) return string {
return salutation & “, ” & name & “!”;
}
args = { name=”Alex”, salutation=”Hi” };
writeOutput( greet(argumentCollection=args) );
UDF vs. CFC Method
- UDFs in a page or include are quick for small utilities and templates.
- CFC methods are better for layered architectures (services, repositories), Encapsulation, reuse, and testing.
- For anything beyond quick helpers, prefer organizing functions inside CFCs.
Syntax Quick reference
CFScript:
function name([modifiers] [typedArgs]) return ReturnType output=false {
// local vars
// logic
return value;
}
Tag-based:
Common modifiers and attributes:
- access: public, package, private, remote
- return/returntype: string, numeric, boolean, array, struct, query, any
- output: true/false
- hint: short description for documentation
- required, Default values on arguments
Key Points
- A ColdFusion UDF is a custom function you define in CFML to encapsulate reusable logic.
- Define UDFs in CFScript or tag-based syntax; both support types, defaults, and validation.
- Keep functions pure, small, and focused, and store them in CFCs for larger applications.
- Use output=false, typed arguments, local scoping, and clear hints as standard practice.
- Leverage closures and higher-order functions for concise data transformations where appropriate.
FAQ
What’s the difference between a UDF and a method in a CFC?
A UDF is any user-defined function, including those defined in templates or includes. A method is a UDF defined inside a ColdFusion Component (CFC). Methods benefit from encapsulation, inheritance, and DI patterns, making them better for larger or layered applications.
Can I call a UDF across multiple templates?
Yes. Place the UDF in a shared include file and cfinclude it, or better, organize it in a CFC and create an instance to call the method. You can also expose it through application-wide scopes, but that increases coupling.
Should I use cfscript or tag-based syntax for UDFs?
Both produce the same outcome. cfscript is concise and popular in modern codebases. Tag-based syntax can be more readable for teams used to CFML tags. Choose one consistently based on team Standards.
Why is output=”false” recommended?
Functions that write directly to the response buffer can cause performance and layout issues, especially when included multiple times or used in APIs. output=”false” encourages returning strings or data for the caller to output explicitly.
Do UDFs support default argument values and named arguments?
Yes. You can define defaults (e.g., boolean lowercase=true) and call functions with named arguments for clarity. You can also pass arguments via argumentCollection for dynamic calls.
