Here's an interesting problem we had to solve recently. A customer came to us with a suite of ecommerce sites on a single server. The sites were set structurally with a core set of code that supported all the sites and then individual templates that handled the layout and design. This is actually pretty common. The folder structure allowed for site specific stuff to go in the site folder while all the common stuff (everything but specific images and layout stuff) went into the site folders.
The application file specific to each site set up the variables needed for that site, then all of the heavy lifting code was called from the "core" folder using includes, custom tags or CFCs. The idea here is to be able to affect the application code of all 50 sites on the server with a single deployment. This is an idea I endorse although there are other ways of doing it. For the scope of this suite of sites it seemed an acceptable solution.
The problem came when we wanted to run code directly from outside the application (meaning the core) without first running it through the application.
The task was to use Lyla Captcha to protect some forms that appeared on every site. Now in case you don't know about Lyla, the code (at least the code we have) uses the application scope. It creates an image "on the fly" with an embedded text string in it and through the magic of cfcontent it is delivered to the page. Since the forms we were using were shared by all the sites we obviously wanted to put Lyla into the core. But the way the site1 folder relates to the core makes this difficult. Why? Let's look at the layout of these folders and applications. For example, application.cfm (or .cfc) in site1 might have something like this:
But let's turn to Lyla for a moment. Lyla works by placing a cfm file as the source of an IMG tag as in (and I'm borrowing liberally from Ray's "quick and dirty" guide here):
But wait! I noticed that there is a "shared_images" folder inside the core. A quick glance at IIS shows that the shared images folder is mapped with a virtual directory named "sharedimg" for all site deployments. This is also a fairly common feature of this type of setup. The core code uses images (buttons, spacers, maybe even product images) that are common to all of the sites. Developers can then easily create a library of images that can be used by accessing a single virtual directory. In other words, from the core code they can safely use:
Of course we could just throw an application.cfm file into the /core folder, but the problem is that lyla matches its hash codes to the ones created within the application of the calling page. In other words, it depends on the form and the Captcha.cfm file being part of the same application.
So how do we manage to get something called from outside of the application to share the application scope with it? We use the magic of the application name.
You may not know this but the "name" of the application binds all of its objects and variables together. If you want the skinny on that tidbit check out my post on Application Variables. For now let's just say it this way: If you name 2 different applications the same they will share the same scope - changes to an application variable in one of them will result in that same variable being changed in the other. Indeed, they are the same memory reference. So how do we use this to our advantage?
Now this is a bit messy and not a "perfect" solution. A perfect solution would probably involve rewriting Lyla to suite our application. We took the following steps. First we put Captcha.cfm in the shared_images folder. Next we altered the URL string in the img tag in the form (in the /core/view/ folder) to:
The result was that our captcha.cfm code mapped itself automatically to the application we previously created in the "site1" or "site2" folders and, if Lyla wasn't instantiated, took care of creating the application scoped object for us as well. All of our Lyla code went into the core and we had no need to touch the site folders or site deployment code. Most importantly, the roll out of this code was accomplished in one step - by deploying the core repository - instead of the need to deploy both the core and the site code as well.
Many of you will say this is not a perfect solution because it breaks some rules (design pattern rules for example). But this problem and solution scenario illustrates an axiom of web development that is often overlooked. Web applications evolve organically over time. To be cost effective it is often necessary to continually support legacy code and make improvements without rewriting large portions of the code. This is simply the nature of web development in the wild. We are often tasked with finding innovative solution to ticklish problems that still conform to a standard of practice (or at the very least don't deviate wildly from it).