|
Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon. |
|
View previous topic :: View next topic |
Author |
Message |
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Mon Nov 10, 2003 9:05 pm Post subject: |
|
|
I updated the code again to reflect messju's suggestion regarding an early bailout. I realize why I switched away from this originally: Smarty ignores what you pass back to it during the first iteration of the block--it is only concerned if it should continue processing the block. Thus, to get output for that case, I had to use echo which I typically wouldn't want to do in a plugin function. Still, this works and it is probably easier to see what is happening now.
Still looking into atu's issue regarding sub-template regeneration.
EDIT: This really isn't the greatest solution because it doesn't capture the sub-template code -- just the results. Is there a way to efficiently get the source code at runtime? Alternatively, is there a way to convert this block function into a compile time function? |
|
Back to top |
|
andre Smarty Pro
Joined: 23 Apr 2003 Posts: 164 Location: Karlsruhe, Germany
|
Posted: Tue Nov 11, 2003 12:51 pm Post subject: |
|
|
Mhmm... this is really a nice solution, boots
This is possibly very helpful for a problem I'm just investigation in my own application. Thanks |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Tue Nov 11, 2003 3:36 pm Post subject: |
|
|
@andre: really it reminds me of this post (and your follow-up) from the nice discussion that came out of your original cache modification ideas for Smarty. I can't find it now, but I seem to recall that somewhere in the discussion the topic of using cache id identifiers within the template (much as atu suggests) was brought up and that this was seen as a bad thing as it forces the designer to consider what should properly be developer issues.
I see the idea of subtemplates as useful--but the cache grouping info should be settable outside of the template itself. The first thing is that the caching and the subtemplate concepts ought be separate ideas and the second thing is that the developer should have a way to define cache categories that the designer could then refer to but whose settings were solely in control of the developer. I think this could lead to some interesting opportunities. For example, I would more easily be able to determine regeneration of a subtemplate because I would essentially have a lookup table I could refer to before I open the template. I could also store the separate cache_ids used in a cached template in the cache_info header of the cache image so as to satisfy regeneration of subtemplate portions.
Further, I really think my code needs to be updated to write actual subtemplate code (not results) or that someone should have a stab at messju's idea (which should work that way) or perhaps suggest an alternative.
Last edited by boots on Wed Nov 12, 2003 2:51 am; edited 1 time in total |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Wed Nov 12, 2003 1:01 am Post subject: |
|
|
As a quick follow-up, messju made a brilliant suggestion on IRC--register the plugin as non-cacheable. That way it will work like insert (with associated performance cost) but would actually still cache since the plugin explicitly caches its data. I don't think it gets simpler than that
EDIT: I haven't tested this hypothesis yet, though I am starting to wonder if it will indeed work as a consequence of the fact that I store results and not template source in the subtemplates. If anyone tests this, please post your findings.
EDIT2: messju believes this will work. I'm going to let others continue this for now
On-the-other-hand, I think that if you didn't cache the outer template at all, you would get similar results.
Further, in the end, I don't think this is a caching issue as much as I see it as introducing sub-templates. I belieive atu rightly pointed out in his original post that the existing caching scheme would work fine if only he used separate templates.
On a more trivial note, I've had second thoughts about the name for the plugin. I chose "sec" to be short for "section" which is obviously already taken. I think it is better called "sub" or "subtpl" or "subcache" or something like that.
Cheers! |
|
Back to top |
|
messju Administrator
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
Posted: Mon Nov 17, 2003 10:31 am Post subject: |
|
|
boots wrote: | EDIT: I haven't tested this hypothesis yet, though I am starting to wonder if it will indeed work as a consequence of the fact that I store results and not template source in the subtemplates. If anyone tests this, please post your findings.
EDIT2: messju believes this will work. I'm going to let others continue this for now |
i didn't look that close at the code. imho it does not work as it is now. i didn't notice you write a template and fetch it afterwards. you are correct: now the output would be parsed a second time in the fetch()-call. that's not desirable and sometimes even dangerous. instead of writing to the templates-dir and fetch()-ing one should use write_cache_file() to write the contents. but that may cause average to major headaches regardings inserts and non-cached-plugins. |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Mon Nov 17, 2003 5:44 pm Post subject: |
|
|
messju wrote: | instead of writing to the templates-dir and fetch()-ing one should use write_cache_file() to write the contents. but that may cause average to major headaches regardings inserts and non-cached-plugins. |
ha ha -- "average to major headaches" -- hilarious! True, though. Without the source template being available, there is no way to independantly regenerate the cache for the generously named "subtemplates". The only way I can see around this is to chop the template into source bits. I've hinted that I don't see that as trivial--but something that may be possble using the approach messju suggested at the start of this thread.
I'm glad that this came out because the last time I discussed this on IRC with messju, I was completely confused as I felt the code was faulty from the start but messju somehow convinced me that it would work as-is
I do think that there are certain cases where there may be merit for the solution I proposed but I will let others decide its fate. FWIW, below is a code update that incorporates changes I received from messju to meet the concerns in his last post. I am posting it here instead of in my original post since it no longer operates in the same way. Indeed, in some ways this plugin is utterly superfluous; however, if you intend to cache a static content piece (or "clip") it may have some merit for you. I have changed the name for this plugin to {clipcache} both to differentiate it from {sec} and to better reflect its purpose.
[php:1:e6d2330acc]/**
* block.clipcache.php
*
* Create a cache image for a "clip" from a template.
*
* @author boots, messju
* @version 0.1.5 2003-NOV-17
* @since 2003-NOV-10
* @see http://www.phpinsider.com/smarty-forum/viewtopic.php?p=5902#5902
*
* ----------------------------------------------------------------------------
* @param group required specify cache build group
* @param id required id within the cache build group
* @param ttl required time to live for template part/group
* @param template required expects $smarty.template
*/
function smarty_block_clipcache($params, $content, &$smarty, &$repeat)
{
// validation
extract ($params);
foreach (array('id', 'group', 'template', 'ttl') as $required) {
if (!isset($$required)) {
$smarty->trigger_error("clipcache block: $$required param missing. Aborted.");
return;
}
}
$name = "$template.$id"; // clip cache target name
if (!isset($content)) {
// first iteration through block, so no content was processed
// try and read from the cache (if succeeds, do not re-enter block)
$_params = array(
'tpl_file' => $name
, 'cache_id' => $group
, 'compile_id' => $smarty->compile_id
);
require_once SMARTY_DIR.'core'.DIRECTORY_SEPARATOR.'core.read_cache_file.php';
$_caching = $smarty->caching;
$smarty->caching = 2; // cache by individual lifetime
if (@smarty_core_read_cache_file($_params, $smarty)) {
$repeat = false;
echo $_params['results']; // we do this since we can not use return here
}
$smarty->caching = $_caching;
} else {
// cache-miss: cache the clip
// save smarty environment as proposed by calling template
$_ttl = $smarty->cache_lifetime;
$smarty->cache_lifetime = $ttl;
$_caching = $smarty->caching;
$smarty->caching = 2;
$_id = $smarty->cache_id; /* note: cache_id needs to be saved as it can differ from id used for outer template */
// write the cache image
$_params = array(
'tpl_file' => $name
, 'cache_id' => $group
, 'compile_id' => $smarty->compile_id
, 'results' => $content
);
require_once SMARTY_DIR.'core'.DIRECTORY_SEPARATOR.'core.write_cache_file.php';
@smarty_core_write_cache_file($_params, $smarty);
// restore smarty environment as proposed by calling template
$smarty->caching = $_caching;
$smarty->cache_lifetime = $_ttl;
$smarty->cache_id = $_id;
return $content;
}
}[/php:1:e6d2330acc]
This is completely untested by me. Thanks once again to messju for his suggestions and efforts |
|
Back to top |
|
messju Administrator
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
Posted: Mon Nov 17, 2003 6:08 pm Post subject: |
|
|
@boots:
boots wrote: | [php:1:56be25b47d]$_id = $smarty->cache_id; /* note: cache_id needs to be saved as it can differ from id used for outer template */ [/php:1:56be25b47d] |
to my knowledge there is no property $smarty->cache_id, so there is no need to save it. |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Mon Nov 17, 2003 6:53 pm Post subject: |
|
|
messju wrote: | to my knowledge there is no property $smarty->cache_id, so there is no need to save it. |
I thought you'd catch that, messju I was under the impression that cache_id worked like compile_id, but you would certainly know more about that than I.
I won't bother to update the code at this point since it is inconsequential. |
|
Back to top |
|
boots Administrator
Joined: 16 Apr 2003 Posts: 5611 Location: Toronto, Canada
|
Posted: Fri Apr 08, 2005 8:57 am Post subject: {clipcache} improved |
|
|
May 17, 2006 EDIT: THIS POST HAS BEEN SUPERCEDED. Code is now at http://smarty.incutio.com/?page=ClipCache
May 3, 2006 EDIT: new version 0.1.6 -- minor bug fixes. Still beta
Sep 22, 2005 EDIT: new version 0.1.5 -- directory writing issues should be resolved. Expects that use_sub_dirs=true. Still beta
Sep 22, 2005 EDIT: new version 0.1.4 -- Much improved behviour as it now writes template fragments to a required subdirectory 'clipcache' in your compile directory. You must ensure that the directory exists and is writable. Still beta as another update is needed to support template subdirectories (eg: foo/bar.tpl)
July 9, 2005 EDIT: new version 0.1.3 -- minor updates, but still beta.
note: Requires Smarty 2.6.10 or later.
Well, its been a long time but here is an update. This version of the code is more along the lines of messju's original proposal. This particular incantination is built from a prefilter and a function plugin that is registered as non-cached.
Features, Improvements
* Main template can be cached or not;
* The cache_lifetime of the main template need not be less than the cache lifetimes (ttl's) of the clipcache's;
* Writes actual template source to a separate template file (filename#id);
* No longer need to specify the template name.
Caveats* Not resource safe;
* Does not support multiple template_dirs.
The {clipcache} blocks are essentially "removed" from the original template in that the compile file for the template no longer includes the clipcache code directly but rather fetch()'s it from the clipcache's own template file which is created during the prefilter stage. This should imply that clipcache blocks can still see the scope of the original parent; unfortunately, since they are fetched, they behave like an {include}'d template and therefore have local scopes. Assignments made in a {clipcache} block aren't seen in the original template even though they both originate from the same file. Its always something
Notes
You can use the {include_clipcache} function without the prefilter if you want to simply use this as a method to have private caching of your included templates (which the core {include} does not support). eg: Code: | {include_clipcache file="template.tpl" cache_id="cache|group" cache_lifetime=$cache_lifetime} | Note that the {include_clipcache} tag uses different parameters than the prefilter {clipcache} and is more in line with the standard Smarty::display().
Some of you will recall that Andre & Freestyler combined to come up with some variations of a cache handler that works well with {clipcache} setups. I have updated that code to work with eAccelerator. The relevant thread is here.
Installation
Put the plugins in your $smarty->plugins_dir folder. The optional extended Smarty class (Smarty_ClipCache) has methods to register and unregister clipcache features.
Sample Code
example.php[php:1:c9f0d92ac8]<?php
include 'Smarty.class.php';
include 'Smarty_ClipCache.class.php';
$smarty = new Smarty_ClipCache();
// configure to suit your setup
$smarty->register_clipcache();
$smarty->caching = 1;
$smarty->cache_lifetime = 20;
$smarty->display('example.tpl');
?>[/php:1:c9f0d92ac8]example.tpl Code: | <h2>Very Simple Example</h2>
{**
* Plain Jane static content. As usual, this will be refreshed at the frequency
* specified for this template. It should be noted that due to the way the caching
* system works, refresh frequences will be approximate and typically slightly
* greater than the specified amount. This means that template refreshes between
* clips and the main template won't be lock-step.
*}
<div>
Timestamp 1: {$smarty.now|date_format:"%Y-%m-%d %H:%M:%S"}
This first timestamp will refresh every 20 seconds
which is the cache_lifetime of this template.
</div>
{**
* Create a clipcache block.
*
* A clipcache block has its own private refresh frequency and cache id. To
* enable this, the block is written to a separate template file at compile time
* which is then compiled separately.
*
* Note that the following parameters are required:
* @id unique identifier for the block within this template
* @group cache group to associate with the block
* @ttl time-to-live for the block, in seconds
*
* Also note that the close tag must be a duplicate of the open tag!
* Sorry, I'm too lazy to fix that for now.
*}
{clipcache id=test group=foo ttl=5}
<div>
Timestamp 2: {$smarty.now|date_format:"%Y-%m-%d %H:%M:%S"}
This clipcache block timestamp is refreshed every 5 seconds.
</div>
{**
* the following is a nested clipcache block. Tread carefully!
*}
{clipcache id=foo group="foo|foo" ttl=10}
<div>
Timestamp 3: {$smarty.now|date_format:"%Y-%m-%d %H:%M:%S"}
This nested clipcache block timestamp is refreshed every 10 seconds.
</div>
{/clipcache id=foo group="foo|foo" ttl=10}
{/clipcache id=test group=foo ttl=5} |
Extended Smarty Class[php:1:c9f0d92ac8]<?php
/**
* Smarty Addon
* @package Smarty
*/
/**
* Smarty_ClipCache (Smarty)
*
* Extended Smarty class to manage {clipache} plugin functionality.
*
* @file Smarty_ClipCache.class.php
* @version 0.1.6 2006-May-03
* @since 2005-APR-08
*
* @author boots {jayboots ~ yahoo com}
* @copyright brainpower, boots, 2004-2006
* @license LGPL 2.1
* @link http://www.phpinsider.com/smarty-forum/viewtopic.php?p=19733#19733
*/
class Smarty_ClipCache extends Smarty
{
function Smarty_ClipCache()
{
$this->Smarty();
}
function register_clipcache()
{
$this->load_filter( 'pre', 'clipcache' );
require_once $this->_get_plugin_filepath( 'function', 'include_clipcache' );
$this->register_function( 'include_clipcache', 'smarty_function_include_clipcache', false );
$write_path = rtrim( $this->compile_dir, "/\\" ) . DIRECTORY_SEPARATOR . 'clipcache' . DIRECTORY_SEPARATOR;
$this->template_dir = (array)$this->template_dir;
if ( !in_array( $write_path, $this->template_dir ) ) {
$this->template_dir[] = $write_path;
}
}
function unregister_clipcache()
{
$this->unregister_prefilter( 'clipcache' );
$this->unregister_function( 'include_clipcache' );
}
}
[/php:1:c9f0d92ac8]
The Plugins
prefilter.clipcache.php [php:1:c9f0d92ac8]<?php
/**
* Smarty plugin
* @package Smarty
* @subpackage plugins
*/
/**
* Smarty clipcache prefilter plugin
*
* Clip template source into a new file and replace with a call for isolated caching
*
* @file prefilter.clipcache.php
* @version 0.1.6 2006-May-03
* @since 2005-APR-08
*
* @author boots {jayboots ~ yahoo com}
* @copyright brainpower, boots, 2004-2006
* @license LGPL 2.1
* @link http://www.phpinsider.com/smarty-forum/viewtopic.php?p=19733#19733
*
* @param string $source
* @param Smarty_Compiler $compiler
*
* This filter grabs compiler blocks and observes the tag following attributes
*
* #param id required unique id of clipcache block within template
* #param group required specify cache build group
* #param ttl required time to live for template part/group
* #param ldelim optional specify the left delimiter to use for included content
* #param rdelim optional specify the right delimiter to use for included content
*/
function smarty_prefilter_clipcache($source, &$compiler)
{
// setup
require_once $compiler->_get_plugin_filepath( 'outputfilter', 'trimwhitespace' );
$ld = $compiler->left_delimiter;
$rd = $compiler->right_delimiter;
$search = "{$ld}\s*clipcache\s+(.*?)\s*{$rd}(.*){$ld}\s*/clipcache\s+\\1{$rd}";
// Pull out the clip blocks
preg_match_all( "!$search!is", $source, $clip_blocks );
$i=0;
$replacements = array();
foreach ( $clip_blocks[1] as $tag_attrs ) {
$params = $compiler->_parse_attrs( $tag_attrs );
foreach ( array( 'group'=>'cache_id', 'id'=>'id', 'ttl'=>'cache_lifetime' ) as $required=>$mapto ) {
if ( !array_key_exists( $required, $params ) ) {
$compiler->_syntax_error( "clipcache: '$required' param missing. Aborted.", E_USER_WARNING );
return;
} else {
$$mapto = $params[$required];
if ( substr( $$mapto, 0, 1 ) == "'" ) {
$$mapto = substr( $$mapto, 1, strlen( $$mapto ) - 2 );
}
}
}
foreach ( array( 'rdelim'=>$rd, 'ldelim'=>$ld ) as $optional=>$default ) {
${"_{$optional}"} = $default;
$$optional = ( array_key_exists( $optional, $params ) )
? substr( $params[$optional], 1, strlen( $params[$optional] ) - 2 )
: $default;
}
// write the clip block file source template
$write_path = rtrim( $compiler->compile_dir, "/\\" ) . DIRECTORY_SEPARATOR;
$file_name = $compiler->_current_file.'#' . $id;
require_once SMARTY_CORE_DIR . 'core.write_file.php';
smarty_core_write_file( array( 'filename'=>$write_path . 'clipcache' . DIRECTORY_SEPARATOR . $file_name, 'contents'=>$clip_blocks[2][$i++], 'create_dirs'=>true ), $compiler );
// prepare replacement source for the clip block
if ( $ldelim == $ld && $rdelim == $rd ) {
$replacements[] = $ld . 'include_clipcache file="' . $file_name . '" cache_id="' . $cache_id . '" cache_lifetime=' . $cache_lifetime . $rd;
} else {
$replacements[] = $ld . 'include_clipcache file="' . $file_name . '" cache_id="' . $cache_id . '" cache_lifetime=' . $cache_lifetime . ' ldelim="' . $ldelim . '" rdelim="' . $rdelim.'"' . $rd;
}
}
// replace clip blocks
$source = preg_replace( "!$search!is", '@@@SMARTY:CLIPCACHE@@@', $source );
smarty_outputfilter_trimwhitespace_replace( "@@@SMARTY:CLIPCACHE@@@", $replacements, $source );
return $source;
}
[/php:1:c9f0d92ac8]function.include_clipcache.php[php:1:c9f0d92ac8]<?php
/**
* Smarty plugin
* @package Smarty
* @subpackage plugins
*/
/**
* Smarty {include_clipcache} function plugin
*
* Includes a template using private caching parameters. Must be registered as non-caching.
*
* @file function.include_clipcache.php
* @version 0.1.6 2006-May-03
* @since 2005-APR-08
*
* @author boots {jayboots ~ yahoo com}
* @copyright brainpower, boots, 2004-2006
* @license LGPL 2.1
* @link http://www.phpinsider.com/smarty-forum/viewtopic.php?p=19733#19733
*
* @param array $params
* @param Smarty $smarty
*
* This function observes the following tag attributes (in $params):
*
* #param file required template file
* #param cache_id required specify cache build group
* #param cache_lifetime required time to live for template part/group
* #param ldelim optional specify the left delimiter to use for included content
* #param rdelim optional specify the right delimiter to use for included content
*/
function smarty_function_include_clipcache($params, &$smarty)
{
// validation
foreach ( array( 'cache_id', 'file', 'cache_lifetime' ) as $required ) {
if ( !array_key_exists( $required, $params ) ) {
$smarty->trigger_error( "include_clipcache: '$required' param missing. Aborted.", E_USER_WARNING );
return;
}
}
// handle optional delimiters
foreach ( array( 'rdelim'=>$smarty->right_delimiter, 'ldelim'=>$smarty->left_delimiter) as $optional=>$default ) {
${"_{$optional}"} = $default;
$$optional = ( array_key_exists( $optional, $params ) )
? $params[$optional]
: $default;
}
// save smarty environment as proposed by calling template
$_cache_lifetime = $smarty->cache_lifetime;
$smarty->cache_lifetime = $params['cache_lifetime'];
$_caching = $smarty->caching;
$smarty->caching = 2;
$smarty->left_delimiter = $ldelim;
$smarty->right_delimiter = $rdelim;
// run the requested clipcache template
$content = $smarty->fetch($params['file'], $params['cache_id']);
// restore smarty environment as proposed by calling template
$smarty->caching = $_caching;
$smarty->cache_lifetime = $_cache_lifetime;
$smarty->left_delimiter = $_ldelim;
$smarty->right_delimiter = $_rdelim;
return $content;
}
[/php:1:c9f0d92ac8]
There is also an experimental compiler version, but this one is slightly cleaner. EDIT (2005-SEP-22): the compiler version is now abandoned.
Last edited by boots on Thu May 18, 2006 7:25 am; edited 9 times in total |
|
Back to top |
|
micha80 Smarty Rookie
Joined: 31 Aug 2005 Posts: 7 Location: Berlin
|
Posted: Wed Sep 21, 2005 11:37 am Post subject: |
|
|
hello @ all,
i'am a new one to smarty but got the problem you have diskussed here. so i decided to solve the problem with the last suggestion. but i got an error now.
Code: | Fatal error: Call to undefined function: file_put_contents() in /usr/local/smarty/plugins/prefilter.clipcache.php on line 60 |
maybe you know this and could help me to fix it. i've searched all smarty files for this function but i wasn't lucky. maybe there is a spelling error or its an own programmed function or something else but i can't get it by myself.
thx a lot, micha |
|
Back to top |
|
messju Administrator
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
Posted: Wed Sep 21, 2005 11:40 am Post subject: |
|
|
file_put_contents() is only available in php 5.0 and later. |
|
Back to top |
|
micha80 Smarty Rookie
Joined: 31 Aug 2005 Posts: 7 Location: Berlin
|
Posted: Wed Sep 21, 2005 11:52 am Post subject: |
|
|
ok, thx for fast answer...
what does this function do? does it only create a file with the parameter "$file_name" and put in the contents from "$clip_blocks[2][$i++]"?
can i replace this with a PHP4 way?
are there other php5 function i have to replace for php4?
thx, micha |
|
Back to top |
|
messju Administrator
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
|
Back to top |
|
micha80 Smarty Rookie
Joined: 31 Aug 2005 Posts: 7 Location: Berlin
|
Posted: Wed Sep 21, 2005 12:30 pm Post subject: |
|
|
sorry, but i have one more question. where did you define the path for the file?
i only see that you pass the filename but how does the function know where to save the file?
thx, micha |
|
Back to top |
|
messju Administrator
Joined: 16 Apr 2003 Posts: 3336 Location: Oldenburg, Germany
|
Posted: Wed Sep 21, 2005 1:08 pm Post subject: |
|
|
micha80 wrote: | where did you define the path for the file? |
are you kidding? $file_name is assigned two lines above the call to file_put_contents(). |
|
Back to top |
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|
|