Skip to content

Commit 6e2136d

Browse files
committed
LDEV-6070 add lucee.formUrlAsStruct.bracket.notation
1 parent 69f1a06 commit 6e2136d

4 files changed

Lines changed: 86 additions & 57 deletions

File tree

core/src/main/java/lucee/runtime/type/scope/ScopeSupport.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424

2525
import lucee.commons.io.log.LogUtil;
26+
import lucee.commons.io.SystemUtil;
2627
import lucee.commons.lang.StringUtil;
2728
import lucee.commons.net.URLDecoder;
2829
import lucee.commons.net.URLItem;
@@ -55,6 +56,7 @@ public abstract class ScopeSupport extends StructImpl implements Scope {
5556
private static int _id = 0;
5657
private int id = 0;
5758
private static final byte[] EMPTY = "".getBytes();
59+
private static final boolean BRACKET_NOTATION_ENABLED = Caster.toBooleanValue(SystemUtil.getSystemPropOrEnvVar("lucee.formUrlAsStruct.bracket.notation", null), true);
5860

5961
/**
6062
* Field <code>isInit</code>
@@ -189,19 +191,25 @@ protected void fillDecoded(URLItem[] raw, String encoding, boolean scriptProtece
189191
}
190192

191193
if (formUrlAsStruct && (name.indexOf('.') != -1 || name.indexOf('[') != -1)) {
192-
List<String> segments = parseFormUrlName(name);
193-
194-
if (segments == null || segments.isEmpty()) {
195-
// Malformed brackets or no segments, treat as literal key
194+
// If bracket notation is disabled and the name contains brackets, treat as literal
195+
if (!BRACKET_NOTATION_ENABLED && name.indexOf('[') != -1) {
196196
_fill(this, name, value, true, scriptProteced, sameAsArray);
197197
}
198198
else {
199-
// Process segments into nested structure
200-
Struct parent = this;
201-
for (int j = 0; j < segments.size() - 1; j++) {
202-
parent = _fill(parent, segments.get(j), new CastableStruct(Struct.TYPE_LINKED), false, scriptProteced, sameAsArray);
199+
List<String> segments = parseFormUrlName(name);
200+
201+
if (segments == null || segments.isEmpty()) {
202+
// Malformed brackets or no segments, treat as literal key
203+
_fill(this, name, value, true, scriptProteced, sameAsArray);
204+
}
205+
else {
206+
// Process segments into nested structure
207+
Struct parent = this;
208+
for (int j = 0; j < segments.size() - 1; j++) {
209+
parent = _fill(parent, segments.get(j), new CastableStruct(Struct.TYPE_LINKED), false, scriptProteced, sameAsArray);
210+
}
211+
_fill(parent, segments.get(segments.size() - 1), value, true, scriptProteced, sameAsArray);
203212
}
204-
_fill(parent, segments.get(segments.size() - 1), value, true, scriptProteced, sameAsArray);
205213
}
206214
}
207215
else {

core/src/main/java/resource/setting/sysprop-envvar.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@
327327
"type": "boolean",
328328
"default": false
329329
},
330+
{
331+
"sysprop": "lucee.formUrlAsStruct.bracket.notation",
332+
"envvar": "LUCEE_FORMURLASSTRUCT_BRACKET_NOTATION",
333+
"desc": "Controls whether form and URL parameters with bracket notation (e.g., user[name]=foo) are parsed into nested structs when formUrlAsStruct is enabled. When true (default), bracket notation like user[name] is parsed as user.name. When false, bracket notation is treated as literal keys for backwards compatibility",
334+
"category": "application",
335+
"type": "boolean",
336+
"introduced": "7.1",
337+
"default": true
338+
},
330339
{
331340
"sysprop": "lucee.full.null.support",
332341
"envvar": "LUCEE_FULL_NULL_SUPPORT",

test/tickets/LDEV5934.cfc

Lines changed: 0 additions & 47 deletions
This file was deleted.

test/tickets/LDEV6070.cfc

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="ajax,form,url" {
55
}
66

77
function run( testResults, testBox ) {
8-
describe( "LDEV-6070: Support bracket notation in form/URL parameters", function() {
8+
describe( "Bracket notation in form/URL parameters", function() {
99

1010
it( "should parse simple bracket notation into nested struct", function() {
1111
var result = _internalRequest(
@@ -201,6 +201,65 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="ajax,form,url" {
201201
});
202202

203203
});
204+
205+
describe( "Array notation with [] suffix (existing functionality)", function() {
206+
207+
it( "should handle tags[] with struct format (array value)", function() {
208+
var result = _internalRequest(
209+
template: "#uri#/test.cfm",
210+
forms: {
211+
"tags[]": [ "java", "cfml", "lucee" ]
212+
}
213+
);
214+
expect( result.filecontent ).toInclude( "tags=java,cfml,lucee" );
215+
});
216+
217+
it( "should handle tags[] with query string format (multiple params)", function() {
218+
var result = _internalRequest(
219+
template: "#uri#/test.cfm",
220+
forms: "tags[]=java&tags[]=cfml&tags[]=lucee"
221+
);
222+
expect( result.filecontent ).toInclude( "tags=java,cfml,lucee" );
223+
});
224+
225+
it( "should handle single value with tags[]", function() {
226+
var result = _internalRequest(
227+
template: "#uri#/test.cfm",
228+
forms: "tags[]=lucee"
229+
);
230+
expect( result.filecontent ).toInclude( "tags=lucee" );
231+
});
232+
233+
it( "should handle nested array notation user[tags][]", function() {
234+
var result = _internalRequest(
235+
template: "#uri#/test.cfm",
236+
forms: "user[tags][]=foo&user[tags][]=bar"
237+
);
238+
expect( result.filecontent ).toInclude( "user.tags=foo,bar" );
239+
});
240+
241+
it( "should handle deeply nested array notation user[prefs][tags][]", function() {
242+
var result = _internalRequest(
243+
template: "#uri#/test.cfm",
244+
forms: "user[prefs][tags][]=foo&user[prefs][tags][]=bar"
245+
);
246+
expect( result.filecontent ).toInclude( "user.prefs.tags=foo,bar" );
247+
});
248+
249+
});
250+
251+
describe( "Original reported issue from forum", function() {
252+
253+
it( "should handle userData[name] and userData[sellerID] from jQuery post", function() {
254+
var result = _internalRequest(
255+
template: "#uri#/test.cfm",
256+
forms: "userData[name]=John&userData[sellerID]=12345"
257+
);
258+
expect( result.filecontent ).toInclude( "userData.name=John" );
259+
expect( result.filecontent ).toInclude( "userData.sellerID=12345" );
260+
});
261+
262+
});
204263
}
205264

206265
private string function createURI( string calledName ) {

0 commit comments

Comments
 (0)