boxlang-cfml-migration
BoxLang for CFML Developers
Overview
BoxLang is a new language with a dual parser — it can parse and run CFML file types (.cfc, .cfm, .cfs) while also offering a modern native syntax via .bx / .bxs / .bxm files. For full CFML behavioral parity, install the bx-compat-cfml compatibility module.
The bx-compat-cfml Compatibility Module
The module patches BoxLang to behave like Adobe ColdFusion or Lucee for legacy code:
# CommandBox install
install bx-compat-cfml
# Auto-install via server.json script
{
"scripts": {
"onServerInitialInstall": "install bx-compat-cfml"
}
}
What it enables/restores:
clientscope supportcfsqltype/cf_sql_*query param syntaxblockfactorquery option (instead offetchSize)- Date comparison at second-level precision (instead of millisecond)
- CFML tag prefix
<cfXXX>alongside<bx:XXX> - Legacy BIF names (
asc,chr,serializeJSON, etc.)
If CFML files start up without this module, BoxLang will still parse them — but behavioral differences (listed below) will apply.
File Types
BoxLang parses all traditional CFML file types:
| Extension | Type | Notes |
|---|---|---|
.cfc |
Component / Class | CFML classes |
.cfs |
Script | CFML scripts |
.cfm |
Template | CFML templates |
.bx |
Class | Native BoxLang class |
.bxs |
Script | Native BoxLang script |
.bxm |
Template | Native BoxLang template |
Components → Classes
CFML components become classes in BoxLang. component and class keywords are interchangeable, but .bx files use class:
// CFML (.cfc)
component {
property name="firstName";
}
// BoxLang (.bx)
class {
property name="firstName";
}
Tags → Components (Template Prefix Change)
BoxLang uses a <bx: prefix in templates instead of <cf:
<!-- CFML -->
<cfif expression>
<cfelse>
</cfif>
<!-- BoxLang -->
<bx:if expression>
<bx:else>
</bx:if>
Default Assignment Scope in Functions
| File Type | Default function assignment scope |
|---|---|
.cfm / .cfc |
variables (CFML behavior) |
.bx / .bxs / .bxm |
local (BoxLang behavior) |
// In a .cfc — assigns to variables scope (CFML compat)
function foo() {
x = 42 // variables.x
}
// In a .bx — assigns to local scope (BoxLang default)
function foo() {
x = 42 // local.x
}
Function output Annotation Default
| File Type | Default output |
|---|---|
.cfm / .cfc |
true |
.bx / .bxs / .bxm |
false |
Accessors Are true by Default
BoxLang enables accessors automatically — no need to declare accessors="true" on CFML classes:
class {
property name="fullName";
}
var user = new User()
user.setFullName( "Luis" ) // generated setter
println( user.getFullName() ) // generated getter
Invoke Implicit Accessors
BoxLang also enables implicit accessor invocation by default — property-style access calls the getter/setter transparently:
user.fullName = "Luis Majano" // calls setFullName()
println( user.fullName ) // calls getFullName()
Annotations (Documentation Comments)
In CFML, doc comment annotations affect function/argument behavior. In BoxLang, doc comments are documentation only — move behavioral annotations to proper @annotation syntax:
// CFML
/**
* @output false
* @brad wood
* @myService my hint here
* @myService.inject
*/
function foo( required any myService ) {}
// BoxLang
/**
* @myService my hint here
*/
@output( false )
@brad( "wood" )
@myService.inject
function foo( required any myService ) {}
Multiple Catch Types
BoxLang supports catching multiple exception types in one catch block:
try {
// ...
} catch ( foo.Exception | bar.Exception | com.luis.majano e ) {
println( e.message )
}
castAs Operator
Use castAs instead of javaCast():
// CFML
javaCast( "int", myValue )
// BoxLang
myValue castAs "int"
Import and Object Resolvers
BoxLang's import supports class aliases and resolver prefixes:
// Standard import
import models.User
// With alias (avoids name collisions)
import java:org.apache.User as jUser
import models.User
var oUser = new jUser()
var appUser = new User()
// Resolvers in extends/implements
class implements="java:java.io.Serializable" {
// ...
}
Built-in resolvers: bx: (default), java:
No client Scope (Without Compat Module)
BoxLang does not implement a native client scope. Use session instead — sessions can be backed by any cache provider and distributed. Install bx-compat-cfml if you need client scope for legacy code.
BIF Renames
| CFML | BoxLang |
|---|---|
asc() |
ascii() |
chr() |
char() |
serializeJSON() |
jsonSerialize() |
deserializeJSON() |
jsonDeserialize() |
getComponentMetadata() |
getClassMetadata() |
createObject Type Change
// CFML
createObject( "component", "models.User" )
// BoxLang
createObject( "class", "models.User" )
JDBC Query Changes
sqltype replaces cfsqltype
// CFML
queryExecute(
"SELECT * FROM users WHERE id = :id",
{ id: { value: arguments.id, cfsqltype: "cf_sql_numeric" } }
)
// BoxLang (preferred)
queryExecute(
"SELECT * FROM users WHERE id = :id",
{ id: { value: id, sqltype: "numeric" } }
)
| Syntax | BoxLang Core Behavior |
|---|---|
sqltype: "numeric" |
✅ Preferred |
sqltype: "cf_sql_numeric" |
Silently treated as "numeric" |
cfsqltype: "cf_sql_numeric" |
❌ Error in core; OK with bx-compat-cfml |
fetchSize replaces blockfactor
// CFML
queryExecute( "SELECT * FROM bigTable", {}, { blockfactor: 100 } )
// BoxLang
queryExecute( "SELECT * FROM bigTable", {}, { fetchSize: 100 } )
Date and Time Handling
BoxLang uses java.time.ZonedDateTime (not java.util.Date) for all date/time values. This means:
- Precision: millisecond-level (CFML was second-level)
- Timezone-aware by default
- Greater internationalization accuracy
// If you need to pass a date to Java code requiring java.util.Date
var legacyDate = toLegacyDate( myDate )
With bx-compat-cfml, date comparison functions revert to second-level precision to match legacy behavior.
Date Addition Rounding
BoxLang correctly rounds date additions to the millisecond. Legacy engines incorrectly rounded sub-second additions to the minute:
var epochDate = parseDateTime( "1970-01-01T00:00:00.000Z" )
var updatedDate = dateAdd( "s", 500/1000, epochDate )
// BoxLang: "1970-01-01T00:00:01.000Z" ✅
// Legacy CFML engines: "1970-01-01T00:01:00.000Z" ❌ (wrong)
structCopy Behavior Change (Lucee)
Lucee's structCopy( cfc ) returned a shallow copy of the CFC. BoxLang returns a struct representation of the properties instead. Use duplicate() for a shallow CFC copy:
// Lucee
var copy = structCopy( myCFC ) // returned CFC copy — no longer works this way
// BoxLang
var copy = myCFC.duplicate() // correct shallow copy
getCurrentTemplatePath() and Relative Includes
BoxLang makes template path resolution consistent across all cases. The "current template path" and relative lookups always resolve to the original source file on disk of the executing code — regardless of whether it's an injected UDF, a closure, or a direct include.
Adobe CF and Lucee disagree with each other for injected UDFs; BoxLang picks one sane, consistent rule.
Auto-casting of Arguments and Return Types
When a function argument or return value is typed as numeric, BoxLang actively casts the value to a real number (not a string that looks like one). This is transparent in most cases but can affect:
getClass()checks on the underlying Java type- Passing values directly to Java methods expecting a specific numeric type
Migration Checklist
- Install
bx-compat-cfmlfor legacy CFML apps before making any changes - Replace
cfsqltypewithsqltypeand stripcf_sql_prefix - Replace
blockfactorquery option withfetchSize - Move doc comment annotations (
@output false, etc.) to proper@annotationsyntax in.bxfiles - Replace
createObject("component", ...)withcreateObject("class", ...) - Update BIF calls:
serializeJSON→jsonSerialize,chr→char, etc. - Replace
<cftag prefix with<bx:in templates being migrated to.bxm - Use
toLegacyDate()only when passing dates to Java APIs requiringjava.util.Date - Replace
structCopy( cfc )withmyCFC.duplicate()if coming from Lucee - Verify function default scoping —
.bxfunctions default tolocal, notvariables - Verify
outputannotation behavior —.bxfunctions default tooutput=false - Leverage
castAsoperator instead ofjavaCast()in new BoxLang code - Use
import java:ClassName as aliasto resolve class name collisions
More from ortus-boxlang/skills
boxlang-functional-programming
Use this skill when working with BoxLang lambdas, closures, arrow functions, higher-order functions, functional array/struct pipelines (map, filter, reduce, flatMap, groupBy, etc.), destructuring, or spread syntax.
10boxlang-code-reviewer
Use this skill when reviewing BoxLang code for quality, correctness, security vulnerabilities, performance issues, style violations, or when providing structured code review feedback following BoxLang best practices and security guidelines.
9boxlang-best-practices
Use this skill when writing, reviewing, or improving BoxLang code to ensure it follows community best practices for naming, structure, scoping, error handling, performance, and maintainability.
9boxlang-classes-and-oop
Use this skill when writing BoxLang classes, components, interfaces, inheritance hierarchies, annotations, properties, constructors, or applying object-oriented design patterns in BoxLang.
9boxlang-web-development
Use this skill when building BoxLang web applications: Application.bx lifecycle, request/response handling, sessions, forms, REST APIs, HTTP clients, routing, CSRF protection, Server-Sent Events, or configuring CommandBox/MiniServer.
8boxlang-configuration
Use this skill when configuring BoxLang runtime settings via boxlang.json, setting environment variables for config overrides, configuring datasources, caches, executors, modules, logging, security, or schedulers — or when helping someone understand the BoxLang configuration system.
8