|
| 1 | +<!-- |
| 2 | +{ |
| 3 | + "title": "Migrating from Classic to Modern Local Scope Mode", |
| 4 | + "id": "local-scope-migration", |
| 5 | + "categories": ["scopes", "variables", "migration"], |
| 6 | + "description": "Guide for safely migrating your Lucee application from classic to modern local scope mode", |
| 7 | + "keywords": [ |
| 8 | + "local scope", |
| 9 | + "variables", |
| 10 | + "migration", |
| 11 | + "scope mode", |
| 12 | + "classic mode", |
| 13 | + "modern mode" |
| 14 | + ] |
| 15 | +} |
| 16 | +--> |
| 17 | + |
| 18 | +# Migrating from Classic to Modern Local Scope Mode |
| 19 | + |
| 20 | +Lucee offers two different modes for managing unscoped variables within functions: Classic and Modern. This document explains the differences between these modes and provides a structured approach for migrating from Classic to Modern mode. |
| 21 | + |
| 22 | +## Understanding Local Scope Modes |
| 23 | + |
| 24 | +The "Local scope mode" setting defines how variables with no explicit scope are handled within functions: |
| 25 | + |
| 26 | +* **Classic Mode (CFML Default)**: Unscoped variables are stored in the variables scope, unless the key already exists in one of the function's local scopes (arguments or local). |
| 27 | +* **Modern Mode**: Unscoped variables are always stored in the local scope, regardless of whether they exist elsewhere. |
| 28 | + |
| 29 | +## Why Migrate to Modern Mode? |
| 30 | + |
| 31 | +Modern mode offers several advantages: |
| 32 | + |
| 33 | +1. **Thread Safety**: Variables are isolated to the function instance, preventing race conditions when component instances are shared across requests. |
| 34 | +2. **Predictable Behavior**: All unscoped variables behave the same way, making code more intuitive and easier to maintain. |
| 35 | +3. **Performance**: Local scope lookups are typically faster than cascading scope resolution. |
| 36 | + |
| 37 | +### Classic Mode Race Condition Example |
| 38 | + |
| 39 | +Consider this component when running in Classic mode: |
| 40 | + |
| 41 | +```javascript |
| 42 | +component { |
| 43 | + function createToken(id) { |
| 44 | + token = createUUID(); // Stored in variables scope |
| 45 | + storeToken(id, token); |
| 46 | + return token; |
| 47 | + } |
| 48 | +} |
| 49 | +``` |
| 50 | + |
| 51 | +If this component is stored in application scope and used across multiple concurrent requests, `token` becomes a shared variable. This creates a race condition where multiple threads could overwrite each other's values. |
| 52 | + |
| 53 | +## Configuring Local Scope Mode |
| 54 | + |
| 55 | +Local scope mode can be configured at several levels: |
| 56 | + |
| 57 | +### 1. Server Level (Lucee Administrator) |
| 58 | +Under "Settings > Scope", set "Local scope mode" to either "Modern" or "Classic". |
| 59 | + |
| 60 | +### 2. Server Configuration (.CFConfig.json) |
| 61 | +```json |
| 62 | +{ |
| 63 | + "localScopeMode": "modern" |
| 64 | +} |
| 65 | +``` |
| 66 | +Or: |
| 67 | +```json |
| 68 | +{ |
| 69 | + "localScopeMode": "classic" |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +### 3. Application Level (Application.cfc) |
| 74 | +```javascript |
| 75 | +this.localMode = "modern"; // or "classic" |
| 76 | +``` |
| 77 | + |
| 78 | +### 4. Function Level |
| 79 | +```javascript |
| 80 | +function test() localMode="modern" { |
| 81 | + // Function body |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +## Migration Strategy: Using Cascading Write Logging |
| 86 | + |
| 87 | +Switching directly to Modern mode may break existing applications that rely on Classic behavior. A safer approach is to: |
| 88 | + |
| 89 | +1. Enable variable scope cascading write logging |
| 90 | +2. Identify and fix all occurrences of unscoped variables |
| 91 | +3. Switch to Modern mode |
| 92 | + |
| 93 | +### Step 1: Enable Cascading Write Logging |
| 94 | + |
| 95 | +Set the following environment variable or system property (This setting only applies to Lucee 6.2.1.82 and above): |
| 96 | + |
| 97 | +**Environment Variable:** `LUCEE_CASCADING_WRITE_TO_VARIABLES_LOG` |
| 98 | +**System Property:** `-Dlucee.cascading.write.to.variables.log` |
| 99 | + |
| 100 | +Example: |
| 101 | +``` |
| 102 | +# Environment variable |
| 103 | +LUCEE_CASCADING_WRITE_TO_VARIABLES_LOG=application |
| 104 | +
|
| 105 | +# System property |
| 106 | +-Dlucee.cascading.write.to.variables.log=application |
| 107 | +``` |
| 108 | + |
| 109 | +This will log all instances where variables are implicitly written to the variables scope. |
| 110 | + |
| 111 | +### Step 2: Analyze Logs and Modify Code |
| 112 | + |
| 113 | +Monitor your application logs for entries like: |
| 114 | + |
| 115 | +``` |
| 116 | +Variable Scope Cascading Write Detected: The variable [token] is being implicitly written to the variables scope at [MyComponent.cfc:42]. This occurs when no explicit scope (such as local, arguments, or variables) is specified in the assignment. |
| 117 | +``` |
| 118 | + |
| 119 | +For each occurrence, decide whether to: |
| 120 | + |
| 121 | +1. **Add explicit variables scope** (if the variable should remain in the component's variables scope): |
| 122 | + ```javascript |
| 123 | + variables.token = createUUID(); |
| 124 | + ``` |
| 125 | + |
| 126 | +2. **Add explicit local scope** (if the variable should be function-local): |
| 127 | + ```javascript |
| 128 | + local.token = createUUID(); |
| 129 | + ``` |
| 130 | + |
| 131 | +### Common Patterns Requiring Attention |
| 132 | + |
| 133 | +#### Component Properties |
| 134 | + |
| 135 | +Properties meant to be stored at the component level need an explicit variables scope: |
| 136 | + |
| 137 | +```javascript |
| 138 | +// Before |
| 139 | +function init(datasourceName) { |
| 140 | + datasourceName = arguments.datasourceName; |
| 141 | +} |
| 142 | + |
| 143 | +// After |
| 144 | +function init(datasourceName) { |
| 145 | + variables.datasourceName = arguments.datasourceName; |
| 146 | +} |
| 147 | +``` |
| 148 | +
|
| 149 | +#### Local Counters and Temporary Variables |
| 150 | +
|
| 151 | +Variables that should be local to a function call: |
| 152 | +
|
| 153 | +```javascript |
| 154 | +// Before |
| 155 | +function processItems(items) { |
| 156 | + result = []; |
| 157 | + for(i=1; i <= arrayLen(items); i++) { |
| 158 | + processed = processItem(items[i]); |
| 159 | + arrayAppend(result, processed); |
| 160 | + } |
| 161 | + return result; |
| 162 | +} |
| 163 | + |
| 164 | +// After |
| 165 | +function processItems(items) { |
| 166 | + local.result = []; |
| 167 | + for(local.i=1; i <= arrayLen(items); i++) { |
| 168 | + local.processed = processItem(items[local.i]); |
| 169 | + arrayAppend(result, processed); |
| 170 | + } |
| 171 | + return result; |
| 172 | +} |
| 173 | +// or simply set local mode explicitly |
| 174 | +function processItems(items) localMode=true { |
| 175 | + result = []; |
| 176 | + for(i=1; i <= arrayLen(items); i++) { |
| 177 | + processed = processItem(items[i]); |
| 178 | + arrayAppend(result, processed); |
| 179 | + } |
| 180 | + return result; |
| 181 | +} |
| 182 | +``` |
| 183 | +
|
| 184 | +
|
| 185 | +### Step 3: Test Thoroughly |
| 186 | +
|
| 187 | +After updating your code: |
| 188 | +
|
| 189 | +1. Test your application thoroughly |
| 190 | +2. Verify that all functionality works correctly |
| 191 | +3. Look for unexpected behaviors or errors |
| 192 | +4. Make sure you no longer get any log entries |
| 193 | +
|
| 194 | +### Step 4: Disable Logging and Switch to Modern Mode |
| 195 | +
|
| 196 | +Once all code has been updated and tested: |
| 197 | +
|
| 198 | +1. Disable the cascading write logging by removing the environment variable or system property: |
| 199 | + ``` |
| 200 | + # Remove environment variable |
| 201 | + unset LUCEE_CASCADING_WRITE_TO_VARIABLES_LOG |
| 202 | + |
| 203 | + # Or remove system property from startup configuration |
| 204 | + # -Dlucee.cascading.write.to.variables.log |
| 205 | + ``` |
| 206 | +
|
| 207 | +2. Switch to Modern mode at your preferred configuration level (server, application, or function). |
| 208 | +
|
| 209 | +
|
| 210 | +## Conclusion |
| 211 | +
|
| 212 | +Migrating to Modern mode improves thread safety and code predictability, but requires careful examination of existing code. Using cascading write logging helps identify potential issues before they become problems, allowing for a smooth transition. |
0 commit comments