Tuesday, 16 September 2014

CFML: weirdness with properties

G'day:
Sigh... didn't take long for me to be writing CFML again. My excuse is this started out being comparison code for a PHP article, but I've run into some issues.

Consider this code:

// Application.cfc
component {
    this.invokeImplicitAccessor = true;
}

// Test.cfc
component {
    property numeric score;

    public function getPropertyMetadata(){
        return getMetadata().properties;
    }

}

// testTest.cfm

test = new Test();

writeDump(test.getPropertyMetadata());

This demonstrates that this sets the property correctly, as the output bears out:

Array
1
Struct
name
stringscore
type
stringnumeric

(and ColdFusion is the same)

Next I want to give it an initial value, so presume the syntax is the same as param:

param numeric someOptionaNumericVar=0;

The equivalent being:

property numeric score=0;

On Railo I get this:

Array
1
Struct
name
stringnumeric
score
string0
type
stringany


Err: no. It's parsed the code as if it was this (I'll use tags for clarity here):

<cfproperty name="numeric" score="0">

(bear in mind that <cfproperty> is one of those constructs which accepts any old attribute/value pair, and any non-standard-CFML ones are treated as metadata).

But Railo's parsed the thing wrong here, IMO. In ColdFusion I get this:

The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request

Invalid definition for Property.

Property must be defined first within component declaration.The CFML compiler was processing:

  • A script statement beginning with property on line 3, column 9.

OK. Well it sux this doesn't work. I'll have to do it this way:

property type="numeric" name="score" default=3;


Next issue.

I have my default value, but ColdFusion seems to deny it's there!

Here's an updated example CFC:

// Test.cfc
component accessors=true {

    property type="numeric" name="score" default=3;

    public function getPropertyMetadata(){
        return getMetadata().properties;
    }

    public function dumpScore(message){
        writeOutput("#message#:<br>");
        writeOutput("Exists in variables scope: #structKeyExists(variables, "score")#<br>");
        writeOutput("Exists in this scope: #structKeyExists(this, "score")#<br>");
        writeOutput("<hr>");
    }

}

And test rig:

// testTest.cfm

test = new Test();

test.dumpScore("Initial state");

score = test.getScore();
writeOutput("Score is: #score#<br>");
test.dumpScore("After get()");

test.setScore(44);
writeOutput("Score is: #test.getScore()#<br>");
test.dumpScore("After set()");

Here's the summary:

  • I default the property to 3
  • I check if it's defined in either variables or this scope
  • I get it
  • I check if it's defined again
  • I set it to something else
  • I check again

Running this on Railo, I get this:
Initial state:
Exists in variables scope: true
Exists in this scope: false


Score is: 3
After get():
Exists in variables scope: true
Exists in this scope: false


Score is: 44
After set():
Exists in variables scope: true
Exists in this scope: false




So it's stored in the variables scope. As one would expect.

But on ColdFusion:

Initial state:
Exists in variables scope: NO
Exists in this scope: YES


Score is: 3
After get():
Exists in variables scope: YES
Exists in this scope: YES


Score is: 44
After set():
Exists in variables scope: YES
Exists in this scope: YES




There are two things wrong here. Firstly: ColdFusion should not be exposing properties as public! They are supposed to be accessed via accessors, after all. Also: it doesn't expose the default correctly in the variables scope to start with.

I used to despair because I'd find bugs in ColdFusion when trying to test something in Railo, and just comparing how ColdFusion does the same thing. Now I'm finding bugs in ColdFusion when I'm not even trying to use CFML!

Summary of bugs:

  • Railo should be able to parse property numeric score=0 as a numeric property called score with a default of zero. Same as it would for a param. It's fair enough if it gets more complex than that, then perhaps the old-school syntax becomes necessary: RAILO-3205.
  • ColdFusion exposes properties via the this scope: 3825535.
  • ColdFusion mishandles property defaults: 3825537.

Back to PHP now.

--
Adam