Overriding Built-In ColdFusion Methods With Custom ColdFusion Component Methods

Posted November 29, 2006 at 1:54 PM by Ben Nadel

Tags: ColdFusion

ColdFusion has a TON of amazing functions built right into. The problem (in a weird way) with some of these functions is that they have great names. This is a good thing most of the time, but sometimes, a function name is so good that you want to use it for another purpose. Image a situation where you have a custom data type. You might want to slap a ToString() method on it, DataType::ToString(). Or what about a bean that has some sort of validation issues. You might want to slap an IsValid() method on it, Bean::IsValid().

If you try to define these methods within the ColdFusion component you are creating as such:

  • <cfcomponent
  • displayname="Bean"
  • output="false">
  •  
  • <cffunction
  • name="IsValid"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="Determines if bean data is valid.">
  •  
  • <!--- For testing just return true. --->
  • <cfreturn true />
  • </cffunction>
  •  
  • </cfcomponent>

... ColdFusion throws the compile-time error:

The names of user defined functions cannot be the same as built-in ColdFusion functions. The name IsValid used for a user defined function is the name of a built-in ColdFusion function.

This is a shame since IsValid() would be such a sweet member method of the ColdFusion component right? You have two options at this point: One, choose a different name or Two, declare the method indirectly using method pointers.

Option one is lame. Why change the name of the method you want just because ColdFusion doesn't like it. The reason you chose the name (and the reason ColdFusion has a method with the same name) is because that name rocks! If that name were a girl, you would take it out the movies and buy it dinner. Let's not run and hide.

Option two is the one I would suggest. It is slightly kludgy but it accomplishes exactly what you want it to do with minimal work. Remember, the errors we get her are compile time errors, not run time errors. Also remember that pretty much everything in ColdFusion is an object, including variables and user defined functions. We can use this to our advantage.

To get around the compile time issue, we simply cannot name the function exactly how we want it to be named. Sorry. But, we can name a variable with that name. Then, at run time, we can set that variable to point towards the method we want. This, for all intents and purposes, is the same thing as defining a method with the chosen name. Here is the above example with our method:

  • <cfcomponent
  • displayname="Bean"
  • output="false">
  •  
  • <!--- Set method access pointers. --->
  • <cfset THIS.IsValid = $IsValid />
  •  
  • <cffunction
  • name="$IsValid"
  • access="private"
  • returntype="boolean"
  • output="false"
  • hint="Determines if bean data is valid.">
  •  
  • <!--- For testing just return true. --->
  • <cfreturn true />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="IsNotValid"
  • access="public"
  • returntype="boolean"
  • output="false"
  • hint="Determines if bean data is not valid.">
  •  
  • <cfreturn NOT THIS.IsValid() />
  • </cffunction>
  •  
  • </cfcomponent>

Notice that in the ColdFusion component's pseudo constructor (the code outside of method definitions), I am creating a public, THIS-scoped variable of the same name (as the member function I want to create). I am setting this variable to be a pointer to the first class object which is our private, VARIABLES-scoped member method. When I declare the method I prepend the name with the "$" to make sure the code compiles. Since the method gets validated at compile time, this ColdFusion component does not have any parsing errors. However, since the pseudo constructor runs a run time, we end up with a public, THIS-scoped method with our desired name.

  • <!--- Create the bean. --->
  • <cfset objBean = CreateObject( "component", "Bean" ) />
  •  
  • <!--- Call test methods. --->
  • #objBean.IsValid()#
  • #objBean.IsNotValid()#

... gives us:

true
NO

Now, when calling this method from within the ColdFusion component (say from the IsNotValid() method), you MUST scope the method call. If you do not, ColdFusion first looks in its list of methods before checking the scopes of the ColdFusion component itself. Not scoping will result in the error:

Parameter validation error for function IsValid. The function allows 3 parameters, but found 0.

Personally, I think scoping all of your method calls is a good idea any way. To me, this ups readability of the code. The down side of this is that since we calling a scoped variable (IsValid) then we have to use the appropriate scope during the method invocation. This however, should not be a big deal, but something to note.

If anyone knows of a way to do this WITHOUT having the intermediary step, I am all ears.




Reader Comments

Dec 8, 2006 at 2:41 AM // reply »
1 Comments

I'd say its better to pick a different name and to bite the bullet rather than using method pointers because doing so breaks inheritance.

If you extend your object and redefine the $IsValid method and do not explicitly redefine the this.IsValid variable then method calls with call the old method. Subclasses shouldn't need to maintain function pointers in super classes either.

How about BeanIsValid() instead?


Dec 8, 2006 at 7:14 AM // reply »
11,246 Comments

Elliot,

You make a good point. Due to my limited OOP experience, I do not always think about a solution's implications within an OOP environment (such as one that would use "extend"tion).

As far as just biting the bullet, I suppose you are correct. However, there are going to be times when I am certain a class will never be extended, and when they do, I can handle it then. I am not quite ready to bite the bullet as of yet... I just don't like the idea of redundancy in method and variable names. If I am in a bean, there really is no reason that any of the methods (99%) of the time should have "bean" or the class's name should be in any variable or method name... if you are in a bean all those methods and variables relate to that bean.

But again, I have limited to experience with such matters, so I might have to come down off my pedestal and do as you suggest. And, to be honest, that is mostly what I have been doing as thw whole pointer thing just occurred to me a few weeks ago and I have been using CFCs since MX6.


Mar 31, 2009 at 8:36 PM // reply »
1 Comments

Thanks Ben.
Just what I needed to override the toString() method in my component.


Sep 30, 2009 at 5:48 PM // reply »
7 Comments

This is really neat, way to think out of the box Ben!

I don't imagine there is any way to do this with ColdFusion tags? I would like to override the cfinclude tag to display the name of the file its including (would be great for tracking down display code within a framework).


Oct 1, 2009 at 7:58 AM // reply »
11,246 Comments

@Ryan,

You can always create a custom tag to do things like this. And, with CF8 and the usage of AttributeCollection, it becomes much easier:

<cf_location url="" addtoken="" />

... and then your tag internals (which I think work for the CFLocation tag):

<cflocation attributecollection="#attributes#" />

Then, of course, you do what ever you want before the CFLocation tag is actually executed.


Oct 1, 2009 at 9:15 AM // reply »
7 Comments

True, but that would require me to go through and change all my code. And the framework's core code.

But if you could override it, you could have each included file displayed with a border around it, with the file name displayed in it as well. Would be awesome for tracking down which display files you need to edit when you want to change something you are looking at.


Oct 31, 2009 at 10:24 PM // reply »
11,246 Comments

@Ryan,

Yeah, it would be kind of cool. I think Railo might let you do that stuff.


Jan 25, 2010 at 11:21 AM // reply »
1 Comments

I have found to my surprise that CF9 seems to allow for overriding system functions without this trick. I haven't found this documented anywhere yet but it doesn't complain about my trace() dump() toString() or any other such function.


Jan 25, 2010 at 9:02 PM // reply »
11,246 Comments

@David,

Really! That is very interesting. I'll have to take a look at the on my local machine. That could lead to some confusing / interesting approaches.


Sep 24, 2011 at 5:22 AM // reply »
2 Comments

@Ben,
David is totally right. In CF9 we can create function with same name that available in coldfusion, only issue with this is we need to scope with this to call same function within CFC.

I am looking for override now() function with additional argument timezone where it will automatically convert current server time to my timezone. I thought same way it may work in Application.cfc as well and it is but not able to call from my cfm/cfc due to no scope available to call Application.cfc function.

Any Idea?


Sep 24, 2012 at 11:11 AM // reply »
3 Comments

@Pritesh
The downside I've found to this is that if within public function a() you call private function b(), where b() is a function name ALSO used by ColdFusion, you can't do this.b() -- Coldfusion reports that it can't find the function b().

At this point, you are compelled to rename b() to something that doesn't collide with the ColdFusion built-in function.


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools