Skip to content

Prevent Smarty from writing compiled templates

Many tools come with an installation / setup / configuration procedure. At this stage directories needed by the tool or its components usually don't yet exist. For Smarty based systems this poses a problem, as Smarty writes compiled templates to a designated directory. So if the installer is built upon Smarty, the developer is presented a hen/egg problem.

Smarty 3.0 introduced a number of built-in resource types, such as file (default), string and eval. file-resources are read from a defined directory (or set of directories), compiled and written to the compiled-cache-directory. string and eval allow passing a template's source as string (without looking for the template on disk). The key difference between them is that eval does not write a compiled-template file to disk.

So, Smarty already has the ability to prevent compiled templates to be cached on disk. Smarty 3.1 introduced a simple API for creating custom resource handlers. It allows you to obtain template sources from virtually anywhere (filesystem, php streams, databases, …) and enables you to mark templates to be recompiled on every invocation.

Internally Smarty 3.1 uses the Smarty_Template_Source to manage the meta-data individual template possess. That class knows the flag recompiled which, if set to true, prevents Smarty from writing the compiled version of that template source to disk.

Smarty 3.1 also introduces the class Smarty_Resource_Recompiled, which if extended, marks all the template source of your custom resource handler to be recompiled on every call. Since PHP is unable to resolve polymorphic inheritance (and PHP 5.4 Traits aren't available yet) we'd have to re-implement the whole file-resource in order to extend Smarty_Resource_Recompiled. Pretty bad Idea.

Knowing that recompiled is nothing but a boolean flag on an object returned by resource handlers, the obvious solution is to proxy the existing file-resource to set the flag "manually":

// extend regular File Resource to suggest a source must be recompiled on every invocation
class RecompilingFileResource extends Smarty_Internal_Resource_File {
    public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template=null) {
        parent::populate($source, $_template);
        $source->recompiled = true;
    }
}

Now all we have to do is make Smarty use RecompilingFileResource instead of Smarty_Internal_Resource_File. The docs on Smarty Resources suggest one can register custom resource handlers using the registerResource() method. What it doesn't mention, is the ability to overwrite internal resource handlers:

// overwrite internal file handler with the one making everything recompile every time
$smarty->registerResource('file', new RecompilingFileResource());

Now, whenever Smarty determines a resource to be loaded through the file-resource, it will call on RecompilingFileResource instead of Smarty_Internal_Resource_File. Make sure the registration of the proxied resource happens before you call the first template!

Please note that this recompiling business costs additional CPU time as Smarty cannot leverage the cache. Make sure to only use this when there is absolutely no other way - like an installation routine which has to run disregarding the software's environment.

Comments

Display comments as Linear | Threaded

Aaron Nielsen on :

Aaron NielsenHi Rodney,

I was pleased to stumble upon your suggestion, but I can't get it to work. Even with the RecompilingFileResource object registered, Smarty is still trying to write files to ./templates_c. I'm using Smarty 3.1.12 on MacOSX 10.7.5.

I can't help but think that I've left something out, but here's what I did: - unzipped and copied Smarty to my web server (in ~user/myapp/lib) - copied index.php, configs/ and templates/ from the Smarty demo/ directory into ~user/myapps - pointed the require() command to the new directory

If I point $smarty->setCompileDir() to somewhere world-writeable, it works every time. If I follow the instructions above to register the RecompilingFileResource and don't set the compile directory at all, it fails every time, because it can't create files in ~user/myapp/templates_c (which is what I'm trying to prevent it from trying to do).

Any idea what I might be missing? I can work around it if I set up a world-readable working directory and point Smarty to that, but I don't really need to cache anything in my application and would really like to avoid it if possible.

Thanks, Aaron

The author does not allow comments to this entry