Smarty Forum Index Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon.

custom compiler and caching

 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> General
View previous topic :: View next topic  
Author Message
jebediah
Smarty Rookie


Joined: 28 Jan 2012
Posts: 16

PostPosted: Fri Jul 27, 2012 7:37 am    Post subject: custom compiler and caching Reply with quote

Hello everyone,

I have a question wrt a custom compiler plugin.

We've got a custom compiler tag at the beginning of each template executing the code necessary to assign the Smary variables for this template.

The call looks like this:
Code:

{ action name="/my/module/action" }


We have registered the compiler like so:
Code:

    $smarty->registerPlugin('compiler', 'action', 'custom_compiler_action', false);


The custom compiler does not do very much, apart from including the module and creating a new instance at the top of the template:

Code:

function custom_compiler_action($params, &$compiler){
  $name = str_replace( array( "'", '"' ), '', $params[ 'name' ] );
  $name = substr($name, 1);//in this case "my/module/action"
  $include_file = $name.'.php';//in this case "my/module/action.php"
  $class = '\\'.str_replace('/', '\\', $name);//in this case "\my\module\action"

  $php = <<<EOT
 <?php
  include_once '$include_file';
  new $class(\$_smarty_tpl);
  ?>
EOT;

  return $php;
}


As you can see, I'm creating a new instance of the class specified (i.e. \my\module\action and passing the $_smarty_tpl variable as a reference to assign the Smarty variables for the current scope.

The problem I've got now is that I'm not able to use any kind of caching Sad

Whenever I set:
Code:

  $smarty->setCaching(\Smarty::CACHING_LIFETIME_CURRENT);
  $smarty->setCompileCheck(false);
  $smarty->display('my_template.tpl', $cache_id); //where $cache_id is a unique ID for the current page to display (MD5 of URL plus necessary parameters)


I just get a page that only contains the HTML surrounding the smarty control tags like if no Smarty variables would have been set at all, e.g.

    - Every {foreach from=$my_var item=$var} loop is passed
    - Every {if $my_var} evaluates to false


I would have expected that smarty caches a pretty much static version of the page (apart from dynamic and nocache blocks) and that this static version contains the output of the evaluated smarty tags.

To explain: assume the action initiated in the custom compiler assigns the following array:

Code:

  $this->_smarty_tpl->assign('integers', array(1,2,3,4,5);


and the template looks like this:

Code:

<ul>
  {foreach from=$integers item=i}
     <li>{$i}</li>
  {/foreach}
</ul>
<p> There are {$integers|count} integers</p>


Then the rendered template WITHOUT caching looks like this:

Code:

  <ul>
     <li>1</li>
     <li>2</li>
     <li>3</li>
     <li>4</li>
     <li>5</li>
  </ul>
  <p> There are 5 integers</p>


but when I enable caching it looks like this:

Code:

  <ul>
  </ul>
  <p> There are  integers</p>


Any ideas would be greatly appreciated.

Bests,
Jeb
Back to top
View user's profile Send private message
U.Tews
Administrator


Joined: 22 Nov 2006
Posts: 5068
Location: Hamburg / Germany

PostPosted: Sat Jul 28, 2012 8:28 am    Post subject: Reply with quote

Two notes:

1. you did set $smarty->setCompileCheck(false); . So Smarty will not detect template source changes and not recompile the template automatically

2. Smarty will never detect modifications of your custom compiler automatically. So after any change you must make sure that the templates get recompiled. Smarty uses for caching different compiled templates as when caching is disabled. May be just the nocache versions got compiled with the latest compiler version. During debugging of the compiler you should perhaps set $smarty->setForceCompile(true);
Back to top
View user's profile Send private message
jebediah
Smarty Rookie


Joined: 28 Jan 2012
Posts: 16

PostPosted: Wed Aug 01, 2012 8:28 am    Post subject: Reply with quote

U.Tews wrote:
Two notes:

1. you did set $smarty->setCompileCheck(false); . So Smarty will not detect template source changes and not recompile the template automatically

2. Smarty will never detect modifications of your custom compiler automatically. So after any change you must make sure that the templates get recompiled. Smarty uses for caching different compiled templates as when caching is disabled. May be just the nocache versions got compiled with the latest compiler version. During debugging of the compiler you should perhaps set $smarty->setForceCompile(true);


Hi U.Tews,

thanks a lot for your reply. I tried disabling compile check and I've forced compilation on the templates.

The result is still the same unfortunately.

What I'm trying to achieve:

I'm trying to define and execute a custom PHP function that sets all the parameters WITHIN the template.

i.e. I want the template to control execution flow of the application. Typically, the PHP application would set all variables and then decide which template to display/render.

I'd like to flip this model on it's head so the PHP application only determines which template to display/render and the template controls which PHP function sets all the variables.

The complication here is that the template is already in the rendering/display process when the PHP function gets called. The template can pass specific parameters to the PHP function and the PHP function will use GET/POST/SESSION variables to determine the value of the variables that will be set and used in the template that is already in the process of being parsed.

The problem I'm having is that it seems like when caching the templates the PHP function (defined within the template) does not get called.

Any other ideas? Maybe there is a better approach to achieve this than a custom compiler class?

Bests,
Jeb[/b]
Back to top
View user's profile Send private message
U.Tews
Administrator


Joined: 22 Nov 2006
Posts: 5068
Location: Hamburg / Germany

PostPosted: Wed Aug 01, 2012 7:55 pm    Post subject: Reply with quote

In your example

Quote:
<ul>
{foreach from=$integers item=i}
<li>{$i}</li>
{/foreach}
</ul>
<p> There are {$integers|count} integers</p>


is executed at render time of the compiled template, because it's not in a nocache section.

At this time $integer is not assigned as you run your compiler function which does that at a later time when the cache file is called.

I think this is in genaral the problem with your approach that caching does not make sense because you run your compiler function as not cacheable. It's like the chicken/egg problem.

If it can be cached you must register it as such.


Code:
$smarty->registerPlugin('compiler', 'action', 'custom_compiler_action', true);
Back to top
View user's profile Send private message
jebediah
Smarty Rookie


Joined: 28 Jan 2012
Posts: 16

PostPosted: Sun Aug 05, 2012 10:02 am    Post subject: Reply with quote

U.Tews wrote:

If it can be cached you must register it as such.
Code:
$smarty->registerPlugin('compiler', 'action', 'custom_compiler_action', true);


Hi U.Tews,

I feel really, really stupid now. That's been the solution and it's working great. I don't know how I could not have seen this.

It's AMAZINGLY fast after the first hit. Reduces the time to display down to about 2ms (even with some nocache sections).

So the example I've provided in the question works perfectly well, IF you set the $cache_able parameter to true in the registerPlugin method as per your suggestion..

When creating the $cache_id and when caching/fetching/displaying the template I'd also recommend to use

a.) cache groups (cf. http://www.smarty.net/docs/en/caching.groups.tpl)
b.) set $smarty->use_sub_dirs = true; (cf. http://www.smarty.net/docs/en/variable.use.sub.dirs.tpl)

depending on the number of your cached templates AND the file-system you're using you might be running into I/O problems otherwise, since per default all templates are stored 'flatly' in your cache directory.

Thanks again for your help!
Jeb
Back to top
View user's profile Send private message
jebediah
Smarty Rookie


Joined: 28 Jan 2012
Posts: 16

PostPosted: Sun Aug 05, 2012 10:51 am    Post subject: Reply with quote

Actually, the whole system is easier to manage using a custom function as opposed to a custom compiler.

i.e. use
Code:
$smarty->registerPlugin('function', 'action', 'custom_function_action', true);


instead of:

jebediah wrote:

Code:
$smarty->registerPlugin('compiler', 'action', 'custom_compiler_action', true);


Then:
Code:

function custom_compiler_action($params, &$compiler){
  $name = str_replace( array( "'", '"' ), '', $params[ 'name' ] );
  $name = substr($name, 1);//in this case "my/module/action"
  $include_file = $name.'.php';//in this case "my/module/action.php"
  $class = '\\'.str_replace('/', '\\', $name);//in this case "\my\module\action"

  $php = <<<EOT
 <?php
  include_once '$include_file';
  new $class(\$_smarty_tpl);
  ?>
EOT;

  return $php;
}


becomes:

Code:

function custom_function_action($params, &$smarty){
  $name = str_replace( array( "'", '"' ), '', $params[ 'name' ] );
  $name = substr($name, 1);//in this case "my/module/action"
  $include_file = $name.'.php';//in this case "my/module/action.php"
  $class = '\\'.str_replace('/', '\\', $name);//in this case "\my\module\action"

  include_once '$include_file';
  new $class($smarty);
}


this has got several benefits:

Imagine you'd like to pass an array to the instantiated class. Then you don't have to use 'complicated' string to array conversions or serialization, but can just simply pass it on to the class (e.g. the parameters)

Code:

function custom_function_action($params, &$smarty){
  if(!isset($params['name'])) return;
  $name = str_replace( array( "'", '"' ), '', $params[ 'name' ] );
  $name = substr($name, 1);//in this case "my/module/action"
  $include_file = $name.'.php';//in this case "my/module/action.php"
  $class = '\\'.str_replace('/', '\\', $name);//in this case "\my\module\action"

  unset($params['name']);

  include_once '$include_file';
  new $class($smarty, $params);
}


From what I understand about smarty (which unfortunately is not as much as I'd like) there is no drawback to using a 'function' as opposed to a 'compiler'.

You're still able to completely control caching behaviour by, e.g.:

Code:

  { action name="/my/module/action" nocache }
  { nocache }
    { $variable1_assigned_within_action }
    { $variable2_assigned_within_action }
    { $variable3_assigned_within_action }
  { /nocache}

  OR simply this

  { $variable1_assigned_within_action nocache }
   
  And for all other variables

   { $other_variable_that_will_be_cached }

  OR control flows:

  {nocache}
    {if $control_variable_assigned_within_action }
    {else}
    {/if}
  {/nocache}


Anyways, thought I should share Smile

Bests,
Jeb
Back to top
View user's profile Send private message
Display posts from previous:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> General All times are GMT
Page 1 of 1

 
Jump to:  
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
Protected by Anti-Spam ACP