I don't use CFLOOP with the condition attribute very much, and when I do, it's usually for simple stuff like a 'while true' loop. However, I ran into a weird quirk today with it, and thought I'd share. Take this code:
[cfloop condition="count LT #config.MAX_IMAGES_PER_IMPORT#">...
Pretty simple, right? As you undoubtedly know, CFLOOP evaluates the condition expression each iteration, so any "static" things can be enclosed in hashes so they're evaluated once (as part of parsing the CFLOOP tag) rather than every iteration. I've done this with the MAX_IMAGES_PER_IMPORT constant.
While I was doing some debugging, I wanted to temporarily truncate imports to a smaller (constant) number, so I did this:
[cfloop condition="count LT #config.MAX_IMAGES_PER_IMPORT / 8#">...
Unlike what you'd expect, this throws a syntax error! Specifically, it's complaining about the slash in there. What's the solution? Change the code to this (move the hashes to use per-iteration evaluation on the expression):
[cfloop condition="count LT #config.MAX_IMAGES_PER_IMPORT# / 8">...
If you're anything like me, a big ol' WTF is probably running through your head. As near as I can surmise, the value of the condition attribute is treated unliked any other string value in CFML. More specifically, variable substitution is performed with hashes, rather than expression evaluation. As my two year old daughter would say "… kinda weird …".
For a while you couldn't use operators within hashes in CF. I think it worked originally, then it didn't work for a couple of versions, and then it worked again.
Actually, I don't think you even need the hashes in this case. :)
Patrick
I don't see the big surprise here.
count LT #config.MAX_IMAGES_PER_IMPORT / 8#
That would obviously throw a syntax error. This isn't a cfloop weirdness, it's a syntax error on your part. Sorry for coming off as sounding like a jerk but before you say something is weird or a bug, you should make sure that it is.
Patrick,
You're right on both counts. The hashes are almost totally irrelevant, as the expression will be evaluated no later than "each iteration". In this simple case, the cost of evaluating the full expression is quite low (division is an expensive arithmetic op, but pales next to a CFC method call). However, if you have a time-consuming expression (like a method call that queries the DB, or worse yet, the filesystem) you could see a nice advantage by using the hashes and only doing the evaluation once, if that suits the need.
Dropping the hashes is exactly what I did, I was more remarking that it's weird that the attribute value behaves unlike every other attribute value I can think of.
Tony,
Why is it obvious that that should cause a syntax error? I consider myself pretty knowledgable about the CF language and it definitely seems at odds with every other piece of syntax I've come across. Perhaps a better question would be this one.
If this is legal:
[cfparam name="x" default="count LT #config.MAX_IMAGES_PER_IMPORT / 8#" />
why isn't this?
[cfloop condition="count LT #config.MAX_IMAGES_PER_IMPORT / 8#" />
Obviously the two have different runtime semantics, but a syntax error can't be caused by those.
That is really strange! If you want to get crazy, this works :)
[cfloop condition="count LT #IIF( true, DE( config.MAX_IMAGES_PER_IMPORT / 8 ), DE(0) )#"]
No need to, but I just wanted to test it.
Certainly more readable (though equivalent) is this:
[cfset x = config.MAX_IMAGES_PER_IMPORT / 8 />
[cfloop condition="count LT #x#">
It's worth mentioning that numbers evaluate to themselves, so you don't need to DE() them inside an IIF() unless you want to. Using DE will save you the cost of evaluating the expression if it's unneeded, but for simple numbers that's usually not helpful, so you can just use the number directly.