View previous topic :: View next topic |
Author |
Message |
SlowFox Smarty Regular
Joined: 02 Oct 2006 Posts: 55
|
Posted: Wed Oct 12, 2011 3:21 pm Post subject: Smarty 3.1 and some caching issues |
|
|
I am in some trouble after moving to Smarty3.1.3. One of my bigger projects uses Smarty for 7 years now and has about 750 individual templates. Due to Smarty2 restrictions and the number of templates, I created my own file structure according to the project needs and wrote a wrapper class based on the Smarty class. This works as follows: templates are divided into "categories" and (if there are still too many in a single category) into a directory tree below them. The place where the template can be found is well defined and the template name does NOT need to be unique throughout the system.
So I have something like (just a small example to illustrate the point):
category "format_xml": order.tpl, receipt.tpl, invoice.tpl
category "format_csv": order.tpl, receipt.tpl, invoice.tpl
category "format_html": order.tpl, receipt.tpl, invoice.tpl
category "page":
directory user: show.tpl, edit.tpl, delete.tpl
directory product: show.tpl, edit.tpl, delete.tpl
directory product/part: show.tpl, edit.tpl, delete.tpl
When switching to 3.1.3 I replacedd my old wrapper by a custom resource handler class which is given category and directory in its constructor and which looks for and returns the appropriate files. Works fine at a basic level. However there appear two different problems:
A) Smarty_Resource::source() and Smarty_Template_Source::getCompiled() both use static caches. They inhibit renderinging of two different templates with the same name. The cache key consists of joined_template_dir (which is constant throughout my usage of Smarty) and $template_resource, which is the (in my case: identical) filename. Bad luck... even trying to use different Smarty objects and/or different resource handlers had no effect, as the caches are static. After commenting out the caching, this issue is solved but I don't really like patching 3rd party software. An idea would be to let the resource handler optionally create a unique identifier for each template which could be used for all the caching issues.
B) templates with the same name are still a problem, as they are not distinguished in the compile-cache directory. I can work around the problem partially by generating different resource types for each category (though this is kind of abusing this feature), but as soon as there are identical files within a single category, this fails miserably. As for now I disabled the compile-cache completely which makes my project work again; however, the performance hit is quite strong. I think this could be solved by the same measure: a template id supported by the custom resource handler to distinguish templates instead of using their mere file name. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Wed Oct 12, 2011 4:20 pm Post subject: |
|
|
so what you need (to solve both problems) is a way for your custom resource handler to modify the template's name. I'll look into it (tomorrow…) _________________ Twitter |
|
Back to top |
|
SlowFox Smarty Regular
Joined: 02 Oct 2006 Posts: 55
|
Posted: Wed Oct 12, 2011 10:44 pm Post subject: |
|
|
globe wrote: | so what you need (to solve both problems) is a way for your custom resource handler to modify the template's name |
Yes, that would be perfectly fine. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Thu Oct 13, 2011 1:27 pm Post subject: |
|
|
So I worked up a solution to the problem: SVN trunk revision 4379.
resource.ambiguous.php is of special interest to you. use buildUniqueResourceName() make your ambiguous templates unique (inject your category, directory, …). Also make sure the $source->filepath and $source->uid are unique accross the board.
Whatever buildUniqueResourceName() returns is used for runtime caching mechanisms. whatever ->filepath and ->uid contain, are used for persistent caching (compiled templates, output cache). (not that you really needed to know this…) _________________ Twitter |
|
Back to top |
|
SlowFox Smarty Regular
Joined: 02 Oct 2006 Posts: 55
|
Posted: Fri Oct 14, 2011 6:37 am Post subject: |
|
|
Wow, cool. Thanks!
Two questions:
1) most likely I just did something wrong, but why is it
Code: | Smarty_Template_Source::getCompiled() {
[...]
$_cache_key = $_template->template_resource . '#' . $_template->compile_id; |
Shouldn't there be a reference to $this->uid? When I change this to
Code: | $_cache_key = $_template->template_resource . '#' . $this->uid . '#' . $_template->compile_id; |
things work as I thought they should do. But perhaps I did miss something?
2) why the double caching in Smarty_Resource::load()?
Code: | // try the instance cache
if (isset(self::$resources[$resource_type])) {
return self::$resources[$resource_type];
}
// try registered resource
if (isset($smarty->registered_resources[$resource_type])) {
if ($smarty->registered_resources[$resource_type] instanceof Smarty_Resource) {
return self::$resources[$resource_type] = $smarty->registered_resources[$resource_type];
}
if (!isset(self::$resources['registered'])) {
self::$resources['registered'] = new Smarty_Internal_Resource_Registered();
}
return self::$resources['registered'];
} |
I can see only two possibilities:
a) one uses only a single Smarty object (as 99% of the people do), then it does not make any difference in performance whether only $smarty->registered_resources[$resource_type] is used, or self::$resources[$resource_type] is set as well.
b) one uses several smarty instances, then it is counterintuitive to say
Code: |
$smarty_1 = new Smarty();
$smarty_1->registerResource(new Smarty_Resource_Something('res_1');
$smarty_2 = new Smarty();
$smarty_2->registerResource(new Smarty_Resource_Something('res_2'); |
and to have both resources available in both smarty objects afterwards (but this is only a question of not understanding your intentions, not actually any problem for me). |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Fri Oct 14, 2011 9:46 am Post subject: |
|
|
1) you're right, I totally forgot about Smarty_Template_Compiled.
2) Well, the caching was intended to speed up the resource handler identification. But you're right, it is possible that handlers allowed in SmartInstance1 could not be in SmartyInstance2 but would still bleed through. I'll investigate this. _________________ Twitter |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Fri Oct 14, 2011 9:56 am Post subject: |
|
|
So I've proven the "resource handler bleeding through" thing. Caching the handlers should ensure that auto-loaded handlers (say resource.mysql.php) are initialized only once (otherwise you might establish multiple connections to a database or some such). So what we've got to add is a way to identify if the cached resource may be used with the given smarty instance. thanks for finding that! _________________ Twitter |
|
Back to top |
|
SlowFox Smarty Regular
Joined: 02 Oct 2006 Posts: 55
|
Posted: Fri Oct 14, 2011 10:49 am Post subject: |
|
|
globe wrote: | Caching the handlers should ensure that auto-loaded handlers (say resource.mysql.php) are initialized only once (otherwise you might establish multiple connections to a database or some such). So what we've got to add is a way to identify if the cached resource may be used with the given smarty instance |
From a users point of view I'd expect something like the following:
A given Template:
Code: | <p>some stuff with: {include file='mysql:subtemplate.tpl'}</p> |
A given Resource:
Code: | class Smarty_Resource_Mysql extends Smarty_Resource_Custom {
public function __construct($dbname);
public function buildUniqueResourceName(...);
public function populate(...);
[...]
} |
Case A: two objects sharing a database connection:
Code: | $resource_mysql = new Smarty_Resource_Mysql('db_1');
$smarty_a = new Smarty();
$smarty_a->registerResource('mysql', $resource_mysql);
$smarty_b = new Smarty();
$smarty_b->registerResource('mysql', $resource_mysql);
|
Case B - two objects with different database connections:
Code: | $resource_a = new Smarty_Resource_Mysql('db_1');
$smarty_a = new Smarty();
$smarty_a->registerResource('mysql', $resource_a);
$resource_b = new Smarty_Resource_Mysql('db_2');
$smarty_b = new Smarty();
$smarty_b->registerResource('mysql', $resource_b);
|
Actually, the second example is not completely esoteric: think of an application with several clients, each with his own corporate layout. You could paint different clients' advertisments on a single page: one frame-template, one handler, but a variety of individual templates behind that, each stored in the respective clients database.
(And regardless of applicability this kind of usage just feels natural for me) |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Sat Oct 15, 2011 10:51 am Post subject: |
|
|
I just landed a patch for this.
Resource and CacheResource handling is now (properly) functioning as (initially intended and) follows:
1. check if the handler is already in smarty-instance-specific cache, if so: done
2. check if the handler was registered to smarty-instance, if so: done
3. check everything else, if a handler meets all criteria, check if an instance of the handler already exists, otherwise create a new instance.
Code: | // plugins/resource.foo.php exists
$smarty1 = new Smarty();
$smarty2 = new Smarty();
$smarty1->fetch('foo:bar.tpl');
$smarty2->fetch('foo:blurp.tpl');
// resource handler of smarty 1 === smarty 2
$smarty1 = new Smarty();
$smarty2 = new Smarty();
$smarty2->registerResource('foo', Smarty_Resource_Foo());
$smarty1->fetch('foo:bar.tpl');
$smarty2->fetch('foo:blurp.tpl');
// resource handler of smarty 1 !== smarty 2 |
_________________ Twitter |
|
Back to top |
|
SlowFox Smarty Regular
Joined: 02 Oct 2006 Posts: 55
|
Posted: Sun Oct 16, 2011 1:58 pm Post subject: |
|
|
rodneyrehm wrote: | I just landed a patch for this |
Thank you so much, this is exactly what I was longing for.
(Using the trunk I've got a problem with the compile time check now, but this is most likely better off in a new thread) |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Fri Oct 21, 2011 6:41 pm Post subject: |
|
|
I just added an option to enable this ambiguity handling. http://code.google.com/p/smarty-php/source/detail?r=4445
Add Code: | $smarty->allow_ambiguous_resources = true; | to your setup. We disabled this by default, since 99% of implementations won't need this (and should not suffer the performance overhead it imposes…) _________________ Twitter |
|
Back to top |
|
|