Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected behaviour with accessors=true on a component

I am trying to use synthesised accessors on a component on Lucee (although this issue seems to be the same on ColdFusion too).

Repro code:

// Person.cfc
component accessors=true {

    property firstName;
    property lastName;

    function init(firstName, lastName){
        variables.firstName = arguments.firstName;
        variables.lastName = arguments.lastName;
    }

}

And the calling code:

// person.cfm
person = new Person("Abigail", "Bowen");
writeDump(person);

Notice how I am not using the synthesised accessors here, I am purely setting the argument values into same-named variables-scoped variables.

However when I run this code, I see this:

Dump output showing properties

Note how the properties have been populated. There's no problem with this, but I'm clearly not understanding how the accessors flag is supposed to work. I thought it was merely intended to synthesise some accessor methods for me (which it has), but that's all.

Also note that if I modify the CFC definition to not set accessors to true, then the dump shows this:

Dump with no properties

So no synthesised accessors (as expected), but also now the properties aren't even being displayed (with the variables-scoped values or not).

I don't really understand this conflation of "properties" and the accessors setting? Surely the accessors setting should only impact whether those accessor methods get created?

If I was only seeing this on one of the platforms, I'd probably put it down to a vagary of how writeDump() interprets the property definitions. But the behaviour is the same on ColdFusion 11, so it really does seem like there's some difference in behaviour I'm not quite getting.

Can anyone explain this? Are there any docs which explain it? If not... um... why not?

My underlying concern here is that the property values are not being stored "properly" and might cause me problems once I implement more of the code.

UPDATE: At least on ColdFusion, it seems to be just a change in writeDump()'s behaviour, because if there are getters for the properties (whether or not the accessors flag is set) then the property values start showing up in the dump. This is not the case on Lucee though, so there's still a question mark there.

For the sake of full disclosure, this question is a summary of a question I also asked on my blog ("CFML: trying to understand accessors"). The duplication is intentional as my blog gets a different audience from that of this site.

like image 250
Adam Cameron Avatar asked Feb 25 '15 23:02

Adam Cameron


2 Answers

Without accessors=true, the property declarations are just metadata.

With accessors=true, the property declarations trigger the generation of getters / setters and thus a property is both a variables scope item and a pair of methods.

In your constructor, you assign to the variables scope items -- which would be the same as using the generated setters -- and when CFML dumps the component, it sees the property metadata and the generated getters and so it displays the values those properties have (since it can easily and safely call the generated getters).

like image 74
Sean Corfield Avatar answered Oct 18 '22 18:10

Sean Corfield


This came up with ACF9. Until then the definition in the property docs was right: cfproperty declarations are just metadata. (see dump(getMetaData()).

In ACF9 this was not fully correct anymore for 3 reasons:

  1. With accessors=true a getter and setter is generated for each property and those accessors read from and write to the variables scope. Cfproperty is not just metadata anymore, but has a direct effect on the behaviour of the instance. I like to think about it as the CF version of real OO properties (introduced by accident).

  2. The cfdump implementation changes its behaviour based on the property declarations. If property name; is defined and method getName() exists (generated or implemented) it is added to the property section of the dump.

  3. The property attributes control the ORM.

Since I got to know those features, I design all my (public) CFCs to look right when dumped, eg. I only use the property declaration (+ getters) when I want to have it visible. In addition, you can implement methods that are only called by the dumps and cost nothing on instantiation:

struct function getDebug(){
    var x = doSomethingExpensive();
    return { "Foo":f, "Bar":b, "Baz":x };
}

//or for a user iterator
string function getName(){
    return qUsers.name[index];
}

Some caveats I know:

  • ACF always calls the getters from the dumps, while in Railo/Lucee the value from the variables scope is shown. Thus, the above examples (getDebug()and getName()) don't work on Railo/Lucee.
  • If the getter is not public or results in an error, the dump shows an empty string for the property (not sure here, maybe the property is missing instead).
  • Property declarations in extended CFCs are ignored. This gave me some headache in ORM entities that use inheritance, because you are not allowed to declare a property twice. Thus, you have no possibility to show a property that is defined in a base CFCs.
  • Railo/Lucee seems to ignore the property types. All accessor accept and return only strings (see getMetaData()).
  • Minor: In ACF when you activate the accessors, but deactivate getter and setter for a property: property name="user" getter="false" setter="false"; it is still visible in the dump - it should be hidden.
like image 43
Walter Seethaler Avatar answered Oct 18 '22 17:10

Walter Seethaler