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":
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:
$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.
The author does not allow comments to this entry
Comments
Display comments as Linear | Threaded
Aaron Nielsen on :
Hi 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