View previous topic :: View next topic |
Author |
Message |
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Mon Apr 28, 2003 12:17 pm Post subject: {nocache} Patch |
|
|
ADMIN NOTE (2003-may-24): CVS users should checkout andre's new patch HERE
UPDATE: (2003-Jun-21): The CVS now includes an alternate to Andre's implementation, though it borrows/uses many of the same ideas that Andre presents here.
Here's a newer version of my "nocache" patch for Smarty 2.5.0:
http://cmsng.sourceforge.net/Smarty/Smarty_nocache.zip
The archive contains both two .diff files for manual patching and ready to use / already patched PHP files.
Features
- Both block and normal functions can be registered as cached or not cached.
- register_block() and register_function() now take a third parameter $cached (default is TRUE). Set it to FALSE to prevent your custom function from caching.
- Cascading cached / not cached blocks is no problem at all.
- Doesn't slow down Smarty if caching is disabled or if you're not using functions which are not cached
Examples
[php:1:4423c80dd0]
// Custom Functions
function smarty_block_nocache($params, $content, &$smarty) {
return $content;
}
function smarty_function_currenttime($params, &$smarty) {
return date("Y-m-d H:i:s");
}
[/php:1:4423c80dd0]
[php:1:4423c80dd0]
// Main Application
$smarty = new Smarty();
$smarty->register_block("nocache", "smarty_block_nocache", false);
$smarty->register_function("currenttime", "smarty_function_currenttime", false);
$smarty->assign("time", date("Y-m-d H:i:s"));
$smarty->caching = 1;
$smarty->display("myTemplate.tpl");
[/php:1:4423c80dd0]
Code: |
{* My Template *}
Example for caching ...
Current Date & Time (cached): {$time}
Current Date & Time (not cached): {currenttime}
Current Date & Time (not cached): {nocache}{$time}{/nocache}
|
Feel free to test it and report bugs |
|
Back to top |
|
Wom.bat Smarty Pro
Joined: 24 Apr 2003 Posts: 107 Location: Munich, Germany
|
Posted: Mon Apr 28, 2003 1:59 pm Post subject: |
|
|
unfortunately, I can't test it at the moment, but I hope a feature like this (both {nocache} blocks and caching property for functions/blocks etc) will make it into one of the next Smarty version great job, andre |
|
Back to top |
|
AZTEK Smarty Pro
Joined: 16 Apr 2003 Posts: 235 Location: Purdue University
|
Posted: Mon Apr 28, 2003 2:48 pm Post subject: |
|
|
Looks good, I will test this when I get home. _________________ "Imagine a school with children that can read and write, but with teachers who cannot, and you have a metaphor of the Information Age in which we live." -Peter Cochrane |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Apr 29, 2003 9:27 am Post subject: |
|
|
Neat!
I haven't looked at the code yet. It sounds like your code optionally implements a block {insert name="..." script="..."} type pattern for functions. At any rate, very clever.
What kind of code do {nocache} blocks get compiled to? The same that {insert} currently produces?
Is it possible to create non-caching versions of plugins or built-in functions or do they need to be wrapped in a {nocache} block?
Can a function be registered as both cachable and non-cacheable, with both variants at work in the same template (perhaps through a globally available parameter that the engine interprets before calling the function.)
eg. can I do something like {html_table cache=false ...} and {include file="..." cache=false}?
Well, I guess I will have to look! Exciting! |
|
Back to top |
|
AZTEK Smarty Pro
Joined: 16 Apr 2003 Posts: 235 Location: Purdue University
|
Posted: Tue Apr 29, 2003 9:58 am Post subject: |
|
|
It looks like all it does is return the code evaluated instead of the cached equvilent but I havent even read into his code yet _________________ "Imagine a school with children that can read and write, but with teachers who cannot, and you have a metaphor of the Information Age in which we live." -Peter Cochrane |
|
Back to top |
|
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Tue Apr 29, 2003 11:08 am Post subject: |
|
|
It's simple:
Code: |
Example for caching ...
Current Date & Time (cached): {$time}
Current Date & Time (not cached): {currenttime}
Current Date & Time (not cached): {nocache}{$time}{/nocache}
|
If caching is ON the above template gets compiled to
[php:1:341109d48b]
Example for caching ...
Current Date & Time (cached): <?php echo $this->_tpl_vars["time"] ?>
Current Date & Time (not cached): <!--{#SMARTYCACHE#currenttime}-->
Current Date & Time (not cached): <!--{#SMARTYCACHE#nocache}--><!--{#SMARTYCACHE#$time}--><!--{#SMARTYCACHE#/nocache}-->
[/php:1:341109d48b]
with "<!--{#SMARTYCACHE#" and "}-->" beeing the tag delimiters.
Then the compiled version is executed. The output is written into the cache file. (Standard Smarty behaviour)
Code: |
Example for caching ...
Current Date & Time (cached): 12:49:15
Current Date & Time (not cached): <!--{#SMARTYCACHE#currenttime}-->
Current Date & Time (not cached): <!--{#SMARTYCACHE#nocache}--><!--{#SMARTYCACHE#$time}--><!--{#SMARTYCACHE#/nocache}-->
|
Now the output will be compiled again: Tags delimited by "<!--{#SMARTYCACHE#" and "}-->" will now be replaced by the not cached PHP functions:
[php:1:341109d48b]
Example for caching ...
Current Date & Time (cached): 12:49:15
Current Date & Time (not cached): <?php echo $tpl->_... ?>
Current Date & Time (not cached): <?php ... ?>
[/php:1:341109d48b]
This is now the final template which get's executed and outputted to the user.
The trick is to compile a template two times. The biggest perfomance issue is that the second compiled version is not yet stored into a file so for each call of the template it get's compiled again But this is not as critical as you may think because normally there are not as many tags that need to be compiled as before. I am using it with a calendar application and it speed up execution time from 7-10 seconds to 1,3 seconds to display all events of the current month
If you are NOT using caching or if you are NOT using any "not-cached" function there won't be a performance issue after applying the patch.
The next step of my patch will be the creation of a separate file where the final output will resist so compiling will be done only once. But I haven't had the time to check this yet.
boots wrote: | Can a function be registered as both cachable and non-cacheable, with both variants at work in the same template (perhaps through a globally available parameter that the engine interprets before calling the function.) |
No and I am not sure if this would be a good idea. The designed shouldn't need to care about caching at all. This is why a also wouldn't recommend using {nocache} block functions. The designed would need to know too much of your internal application structure.
But you could register one function two times. First using caching and second with caching disabled:
[php:1:341109d48b]
$smarty->register_function("cachingtime", "smarty_function_currenttime", true);
$smarty->register_function("currenttime", "smarty_function_currenttime", false);
[/php:1:341109d48b]
Then both can be used in the same template.
boots wrote: | What kind of code do {nocache} blocks get compiled to? The same that {insert} currently produces? |
Nope, in fact the {insert} tags now get compiled as a custom function which was registered as not cached. The old {insert} behaviour was completely removed. That's why you should delete all compiled versions of your templates. |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Apr 29, 2003 11:35 am Post subject: |
|
|
@andre: nice post.
Quote: | If you are NOT using caching or if you are NOT using any "not-cached" function there won't be a performance issue after applying the patch. |
Okay. I think that choices should be made to allow for caching as often as possible. Also, if I'm not caching, why have nocache support ??
I've resorted to multiple pass techniques in the past and I always feel a bit bothered by them. Your technique relies on tags surviving the first compilation, so not only are there two passes, but I take it that you have to do some filtering before and after the first pass.
Quote: | The next step of my patch will be the creation of a separate file where the final output will resist so compiling will be done only once. But I haven't had the time to check this yet. |
You go!
Code: |
$smarty->register_function("cachingtime", "smarty_function_currenttime", true);
$smarty->register_function("currenttime", "smarty_function_currenttime", false); |
I figured I could do that, but I thought it would be interesting to have it selectable at the template level, your concerns not withstanding.
Quote: | This is why a also wouldn't recommend using {nocache} block functions. |
Hmmm. Why not just use insert then?
I will definately look at your code. Thanks for taking the time to explain things here! |
|
Back to top |
|
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Tue Apr 29, 2003 11:52 am Post subject: |
|
|
boots wrote: | Okay. I think that choices should be made to allow for caching as often as possible. Also, if I'm not caching, why have nocache support ?? |
Some of my templates are cached others are not. (I use caching since I have implemented my patch). So it's essential for me to not slow down my old templates in any way but allow new ones to use the nocache functionality.
Quote: | I've resorted to multiple pass techniques in the past and I always feel a bit bothered by them. Your technique relies on tags surviving the first compilation, so not only are there two passes, but I take it that you have to do some filtering before and after the first pass. |
I have modified the _compile_tag() function of Smarty_Compiler to recognize if a tag is cached or not. So "surviving" is perhaps the wrong word because I'm not using any tricks to make them survive the first compiling process. No pre- or postfiltering is required. That's why you need to register functions explicitly to be not cached so the compiler knows them during compilation.
Quote: |
Quote:
This is why a also wouldn't recommend using {nocache} block functions.
Hmmm. Why not just use insert then?
|
I don't like insert either Instead of creating insert functions I would recommend creating just normal custom functions and register them as not cached. This way the behaviour of caching / not caching is completely transparent for your designer.
Example: You have a News page. For each news item you have several comments. It wasn't cached at all but now you decide the news entries should be cached but the number of comments should not. Now tell the designer to replace all {countComments id="News-ID"} by {insert name="countComments" id="News-ID"}? Ok, no really good example but I hope you understand what I mean. Registering "countComments" as not-cached function is fully transparent and you even don't need to change the existing templates. For the designer it doesn't matter what portions of the templates are cached and which are not. |
|
Back to top |
|
Wom.bat Smarty Pro
Joined: 24 Apr 2003 Posts: 107 Location: Munich, Germany
|
Posted: Tue Apr 29, 2003 12:11 pm Post subject: |
|
|
andre:
I am currently doing
3574f626854313961b2ec322ec8308611361b1c35d1757bc5bff7ffc788ca592-CommentInfo.long-news_news.newsId/14
to achieve what you wrote
then, I fetch() the template and give the output to a function which postprocesses the output (outputfilters don't work here, as they are executed BEFORE output is written to the cache)
the ugliest thing on this method is that I have to create an additional PHP-file including the replacements.
I don't just want to print out the number of comments, but something like "no comments", "1 comment", "2 comments"; plus, there are several different formats, for example only the number of comments, or "none", "1", "2", ... even in different languages)
This entire issue has caused serious headache to me so I guess I will have a look into your thingie, as I could then just do
Code: |
{nocache}
{getCommentCount source="news_news.newsId" item=$entries.newsId assign="commentCount"}
{if ($commentCount == 0)}
{if ($_SESSION.language == "DE")}keine Kommentare
{elseif ($_SESSION.language == "EN")}no comments
{/if}
{elseif ($commentCount == 1)}
{if ($_SESSION.language == "DE")}1 Kommentar
{elseif ($_SESSION.language == "EN")}1 comment
{/if}
{else}
{if ($_SESSION.language == "DE")}{$commentCount} Kommentare
{elseif ($_SESSION.language == "EN")}{$commentCount} comments
{/if}
{/if}
{/nocache}
|
but I somehow would like to wait until this is officially integrated into Smarty, as I always prefer to stay as near to official syntax as possible, if you understand what I mean (no blame on your code, I'm sure it kicks ass ) |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Apr 29, 2003 12:54 pm Post subject: |
|
|
Quote: | I have modified the _compile_tag() function of Smarty_Compiler to recognize if a tag is cached or not.
[snip]
Registering "countComments" as not-cached function is fully transparent and you even don't need to change the existing templates. For the designer it doesn't matter what portions of the templates are cached and which are not. |
Cool. I see what you are getting at and I agree that your technique of registering functions as caching or not is superior to the current state of insert .
You make a good point about keeping caching issues out of the template design. I have to agree that this seems like the sane approach.
Heres a Q:
How would I go about implementing a page that was cached but had a block that was not cached. Here's the caveat: the uncached block is actually to be determined by the user priority. Level 1 gets a cached block with a 1 hour refresh; Level 2 gets a cached block with a 30 minute refresh; Level 3 gets a cached block with a 15 minute refresh; Level 4 gets an uncached block.
I guess I could set up a switch in my PHP code and setup smarty as appropriate. Or use a compile_id?? What if I wanted to do this in template ?
What would you suggest? |
|
Back to top |
|
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Tue Apr 29, 2003 1:34 pm Post subject: |
|
|
boots wrote: | How would I go about implementing a page that was cached but had a block that was not cached. Here's the caveat: the uncached block is actually to be determined by the user priority. Level 1 gets a cached block with a 1 hour refresh; Level 2 gets a cached block with a 30 minute refresh; Level 3 gets a cached block with a 15 minute refresh; Level 4 gets an uncached block. |
Same here in my project. I'm going another way. Here's some pseudo code:
[php:1:624d3d0ac7]
/**
* Display a block
*/
function smarty_function_block($block_id, &$smarty) {
$newSmarty = $smarty; // create a copy of smarty object
$newSmarty->caching = 1;
if ($level == 1)
$newSmarty->cache_lifetime = 60*60;
else if ($level == 2)
$newSmarty->cache_lifetime = 60*30;
else if ($level == 3)
$newSmarty->cache_lifetime = 60*15;
else
$newSmarty->caching = 0;
$newSmarty->assign("content", getBlockContent($block_id));
return $newSmarty->fetch(getBlockTemplate($block_id));
}
[/php:1:624d3d0ac7]
[php:1:624d3d0ac7]
/**
* Main Application
*/
$smarty = new Smarty;
// Blocks implement their own caching routine so we set
// caching flag to FALSE
$smarty->register_function ("block", "smarty_function_block", false);
// ... assign some values
$smarty->assign("foo", "bar");
// enable caching for master template
$smarty->caching = 1;
$smarty->cache_lifetime = 60 * 60 * 24; // one day lifetime
$smarty->display("myTemplate.tpl");
[/php:1:624d3d0ac7]
Code: |
{* My Template *}
... some content ...
{block id="whoIsOnline"}
... some content ...
{block id="newestHeadlines"}
... some content ...
|
Hope you understood what I meant?! This is the way I'm using Smarty. Not 100% clean code (copying objects is ugly in my eyes) but it works very nice |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Apr 29, 2003 2:22 pm Post subject: |
|
|
Thanks andre.
One thing I don't get: I thought that once you set a cache lifetime for a given object, you couldn't change it later unless you used a different cache_id. Did you just leave that detail out or is that something else your patch does? |
|
Back to top |
|
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Tue Apr 29, 2003 2:25 pm Post subject: |
|
|
boots wrote: | One thing I don't get: I thought that once you set a cache lifetime for a given object, you couldn't change it later unless you used a different cache_id. Did you just leave that detail out or is that something else your patch does? |
Oops You're right. So set the cache id to the block's ID + $level or something like this.
[php:1:1235a0bf27]
// ... [snip]
$newSmarty->assign("content", getBlockContent($block_id));
$cache_id = $block_id."|".$level;
return $newSmarty->fetch(getBlockTemplate($block_id), $cache_id);
// ... [snip]
[/php:1:1235a0bf27] |
|
Back to top |
|
mohrt Administrator
Joined: 16 Apr 2003 Posts: 7368 Location: Lincoln Nebraska, USA
|
Posted: Tue Apr 29, 2003 2:37 pm Post subject: |
|
|
Quote: |
Cool. I see what you are getting at and I agree that your technique of registering functions as caching or not is superior to the current state of insert .
|
Smarty will be getting some caching facelifts soon, that is the next big project. We have a pretty good idea how it is going to work, and we have Andre's code to compare against. It's all good stuff to have.
The caching will definately be by registeration, keeping things out of the templates as much as possible. You can think of {insert} as just a custom function registered as non-cached.
Monte |
|
Back to top |
|
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Tue Apr 29, 2003 2:59 pm Post subject: |
|
|
Hi Monte,
I have already mailed with Messju and he explained a bit of your ideas to me (in german language )
Sounds very interessting!
An idea I just had and I'm not sure if it would be practical but what if I just replace the not-cached code within a cache file with pure PHP tags so the cached file gets just included if displayed?!
The way I'm doing it now: Cached template:
Code: |
Example for caching ...
Current Date & Time (cached): 12:49:15
Current Date & Time (not cached): <!--{#SMARTYCACHE#currenttime}-->
Current Date & Time (not cached): <!--{#SMARTYCACHE#nocache}--><!--{#SMARTYCACHE#$time}--><!--{#SMARTYCACHE#/nocache}-->
|
The way I could do it:
Code: |
Example for caching ...
Current Date & Time (cached): 12:49:15
Current Date & Time (not cached): <?php $this->_plugins["functions"]... ?>
Current Date & Time (not cached): <?php ... ?>
|
Then I wouldn't need another file. Would this break something? I have to think about it ... |
|
Back to top |
|
|