You could use onMissingMethod()
for this.
component name="myComponent" hint="myComponent.cfc"{
function init( struct dynamicProperties={} ){
dynamicProperties.each( function( name, default ){
variables[ name ] = default;
} );
return this;
}
function onMissingMethod( name, args ){
if( name.Left( 3 ) IS "get" )
return get( name );
if( ( name.Left( 3 ) IS "set" ) AND ( args.Len() IS 1 ) )
return set( name, args[ 1 ] );
cfthrow( type="NonExistentMethod", message="The method '#name#' doesn't exist" );
}
public any function get( required string accessorName ){
var propertyName = parsePropertyName( accessorName );
if( !variables.KeyExists( propertyName ) )
cfthrow( type="NonExistentProperty", message="The property '#propertyName#' doesn't exist" );
return variables[ propertyName ];
}
public void function set( required string accessorName, required any value ){
var propertyName = parsePropertyName( accessorName );
if( !variables.KeyExists( propertyName ) )
cfthrow( type="NonExistentProperty", message="The property '#propertyName#' doesn't exist" );
variables[ propertyName ] = value;
}
private string function parsePropertyName( accessorName ){
return accessorName.RemoveChars( 1, 3 );
}
}
Pass it your struct of property names/default values and it will "listen" for getters/setters that match. Any that don't will result in an exception.
<cfscript>
myDynamicProperties = { A: 0, B: 0 }; // this is a struct of names and default values
mc = new myComponent( myDynamicProperties );
mc.setA( 100 );
WriteDump( mc.getA() ); // 100
WriteDump( mc.getB() ); // 0
WriteDump( mc.getC() ); // exception
</cfscript>
UPDATE 1: Property name array replaced with name/default value struct as init argument to allow default values to be set.
UPDATE 2: If you want to pass an array of structs containing your name/default value pairs e.g.
dynamicProperties = [ { name: "A", default: 1 }, { name: "B", default: 2 } ];
then the init() method would be:
function init( array dynamicProperties=[] ){
dynamicProperties.each( function( item ){
variables[ item.name ] = item.default;
} );
return this;
}
UPDATE 3: If you must use tags and <cfloop>
to set your dynamic properties then this is all you need in your init method:
<cfloop array="#dynamicProperties#" item="item">
<cfset variables[ item.name ] = item.default>
</cfloop>
onMissingMethod
won't fire if you try to invoke the dynamic methods as properties like this:
method = mc[ "set#property#" ];
method( value );
Instead just make the set()
and get()
methods in the component public and invoke them directly:
mc.set( property, value );
mc.get( property );