Get rid of implicit variable access when dynamically calling property setters

I am trying to figure out if there is a way to eliminate all of the implicit variable access like shown in the photo below. Basically what we are doing is looping over the properties and calling the setters dynamically rather than typing out each setter one by one.

Here is an example of what we are doing:


component accessors="true" {
	 property name="first_name";
	 property name="last_name";
	 property name="middle_name";

	 public any function init(first_name = "", last_name = "", middle_name = "") {
	 	var args = arguments;
	 	structKeyList(args).each(function(ele) {
	 		variables["set#arguments.ele#"](variables.args[arguments.ele]);
	 	});
	 	return this;
	 }

}

This is what we get when doing it this way:
image

In the screenshot below we changed out the word “variables” for “this” and get twice as many implicit variable access counts.
image

This might not seem like a big deal for something this small but on some of our modules we sometimes get counts upwards of 60k and I am hoping if we eliminate the implicit variable access we can increase performance. Here is an example where the counts start to balloon up:

image

When using accessors you don’t need an init() function. In fact, it’s quite a bit faster to not have an init function. Also, the properties of the component now have accessors (getFirst_name()) and mutators (setFirst_name()) that can be called on the component.

This is all you really need:

component accessors="true" {
	 property name="first_name";
	 property name="last_name";
	 property name="middle_name";
}

You can initialize the component and set it’s properties like so:

myComponentObj = new path.to.componentName(
        first_name = 'Bob',
        last_name = 'Robertson',
        middle_name = 'Robbie'
);

And, once the component (bean) is instantiated (as myComponentObj) you can easily modify any of it’s values or retrieve any of it’s values:

myComponentObj.setFirst_name( 'Jane' );

and

echo( myComponentObj.getFirst_name() );

There is no need to set the arguments into the variables scope, accessors already does this for you. No need for an init function, no need to do any of what you’re currently doing if you’re using accessors, really.

HTH

1 Like

@ddspringle I wasn’t aware you could do that, it is very cool, thanks. However the example I gave is a lot simpler than what we are actually doing in some places. In some of our code we are not doing it inside of the “init” but rather functions inside the component. We have several objects extending a base component and the base component has a method that goes and sets some of the accessors based on some logic. To rework the current code to allow for this method would not be possible without a major architecture change so is there an alternative way to prevent the implicit variable access?

Another thing I noticed that is not in my original response is that with the method you suggested it ignores the validators. For example I can make a property type numeric but pass in a string and it still gets set.

@Yamaha32088 Unfortunately it’s an all or nothing thing as far as I know. You either set accessors=true to get the accessor/mutator pattern or you don’t set it (or set it to false) and wire up your own getters and setters as needed (which is considerably slower, btw).

Without seeing your code and understanding exactly what you’re doing or what you mean by ‘the base component has a method that goes and sets some of the accessors based on some logic’ I can’t really suggest any workarounds. If you want to shoot me your base component and one of your complex components that make use of it via email I can take a look and see if I can figure out a less invasive way of working around a giant re-engineering effort (denard.springle@gmail.com)

As for setting a type of numeric and being able to populate it with a string… that’s not exactly what I find in my testing. While it doesn’t throw an error as it should, it simply leaves the default value in place (null, in my case). Filed a bug: https://luceeserver.atlassian.net/browse/LDEV-1560

Here’s my test code:

test.cfc

component accessors="true" {

	property name="alice" type="numeric";
	property name="bob" type="boolean";

}

test.cfm

<cfscript>
	testObj = new test( 
		alice = 'james',
		bob = true
	);

	writeDump( testObj );


	testObj = new test( 
		alice = 12,
		bob = false
	);

	writeDump( testObj );
</cfscript>

and the results (on 5.2.3+35):

I haven’t tested 4.5 yet but ACF throws an exception as expected.

EDIT: I just tested 4.5.5+006 and this is an issue there as well. This might be a type coercion issue.