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

ADVANCED: Improving cache read time

 
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 -> Tips and Tricks
View previous topic :: View next topic  
Author Message
boots
Administrator


Joined: 16 Apr 2003
Posts: 5611
Location: Toronto, Canada

PostPosted: Mon Nov 10, 2003 10:10 pm    Post subject: ADVANCED: Improving cache read time Reply with quote

Hi all.

FWIW, here is a simple (*cough* *cough*) technique to get a slightly swifter display time for cache-hits. The trade-off is that cache-misses are more expensive. I doubt that this will work with all of Smarty's features, but it hasn't blown up on me yet.

The basic idea is to create a stripped down Smarty class that has just enough to be able to determine if a cache image exists and if so, to load it. The idea is to treat the new object as if it were your Smarty object, so you do all your setup against the new object. In the case where there is a cache miss, your settings are automagically copied to an actual Smarty object that is created for you.

This is not for everyone and I'm only going to say how I did it--you'll have to roll-up your sleeves to get it to work for you Smile Besides, the class shown is the type that can stand to be autogenerated from the Smarty sources.
EDIT: See my next post in this thread where I do the work for you Wink

Usage:
[php:1:a4a346319b]<?php
/* the first two cases should work, but doesn't attempt to do anything special with the cache */

// equivalent to $smarty = new Smarty;
// note attempts to load Smarty class from SMARTY_DIR
$boot =& new SmartyBoot($smarty);

// equivalent to $smarty = new LocalSmarty($opts);
// note that a custom smarty subclass is used, its path given and
// an optional argument to be passed to the constructor is also given
$boot =& new SmartyBoot($smarty, 'LocalSmarty', MY_APP_DIR.'LocalSmarty.inc', $opts);


/* the following example shows off the class */
$boot =& new SmartyBoot($smarty, 'LocalSmarty', MY_APP_DIR.'LocalSmarty.inc', $opts, $template, $cache_id, $compile_id);
if (!is_object($smarty)) die($smarty); // cache-hits return the cache contents as a string

// since there was a cache-miss, we are left with a normal $smarty object,
// configured the same way that the SmartyBoot object was configured.
// thus, process case for no existing cache-image

// ... do app work
$smarty->display($template, $cache_id, $compile_id);
?>[/php:1:a4a346319b]
The key, of course, is the SmartyBoot class. Note that you may want to create a cleaner version of this without your custom configuration and then create a subclass that has your configs. Also, you can set your class properties directly instead of in the constructor if you prefer. This is only an example Wink
[php:1:a4a346319b]<?php
/**
* SmartyBoot.inc
*
* Minimal Smarty configuration and cache lookup
*/

require_once MY_APP_DIR . 'smarty_cache_mmcache.inc';

/**
* The following is taken from verbatim from the configureation section of Smarty.class.php
*/

define('SMARTY_PHP_PASSTHRU', 0);
define('SMARTY_PHP_QUOTE', 1);
define('SMARTY_PHP_REMOVE', 2);
define('SMARTY_PHP_ALLOW', 3);

// this is for my own needs, suit yourself
define ('MY_TEMP_SMARTY_COMPILED_DIR', ROOT_DIR . 'tmp\\compiled');
define ('MY_TEMP_SMARTY_CACHED_DIR', ROOT_DIR . 'tmp\\cached');

class SmartyBoot
{
/**
* The following is taken from verbatim from the configuration section of Smarty.class.php
*/
/**#@+
* Smarty Configuration Section
*/

/**
* The name of the directory where templates are located.
*
* @var string
*/
var $template_dir = 'templates';

/**
* - include the entire configuration section but not the private members.
* - DO include the two private members $_cache_info, $_cache_serials
*/

/**
* - include the following functions from Smarty.class.php
* _get_auto_id, _get_auto_filename, _read_file
* _parse_resource_name, _fetch_resource_info
* _get_plugin_filepath
*/

/**
* Finally the custom functions. Note the hacked version of is_cached()!
*/

/**
* constructor
*/
function SmartyBoot(&$smarty, $classname='Smarty', $classpath=SMARTY_DIR, $opts='', $template='', $cache_id=null, $compile_id=null)
{
$this->config_dir = MY_ROOT_DIR.'configs';
$this->template_dir = array(MY_ROOT_DIR.'templates', MY_ROOT_DIR.'data');
$this->compile_dir = MY_TEMP_SMARTY_COMPILED_DIR;
$this->plugins_dir = array(MY_ROOT_DIR.'plugins', SMARTY_DIR.'plugins');

$this->cache_dir = MY_TEMP_SMARTY_CACHED_DIR;
$this->cache_handler_func = 'smarty_cache_mmcache';

$this->default_resource_type = 'file';

//$this->use_sub_dirs = false;
//$this->security = true;

$this->cache_lifetime = 60;
$this->compile_check = true;
$this->force_compile = false;
$this->caching = true;
$this->debugging = false;

if (empty($template) || !($smarty=$this->is_cached($template, $cache_id, $compile_id))) {
$smarty = $this->get_smarty($classname, $classpath, $opts);
$this->configure_smarty($smarty);
}
}

/**
* Creates and returns a smarty instance
*/
function &get_smarty($classname='Smarty', $classpath=SMARTY_DIR, $opts='')
{
if ($classpath == SMARTY_DIR) {
$classpath .= 'Smarty.class.php';
}
include_once $classpath;
if ($opts=='') {
$smarty =& new $classname;
} else {
$smarty =& new $classname($opts);
}
return $smarty;
}

/**
* copy configuration to passed smarty instance
* @param object $smarty
*/
function configure_smarty(&$smarty)
{
foreach ((array)$this as $k=>$v) {
$smarty->$k = $v;
}
}

/**
* test to see if valid cache exists for this template
*
* @param string $tpl_file name of template file
* @param string $cache_id
* @param string $compile_id
* @return string|false results of {@link _read_cache_file()}
*/
function is_cached($tpl_file, $cache_id = null, $compile_id = null)
{
if (!$this->caching)
return false;

if (!isset($compile_id))
$compile_id = $this->compile_id;

$_params = array(
'tpl_file' => $tpl_file,
'cache_id' => $cache_id,
'compile_id' => $compile_id
);
require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.read_cache_file.php');
// this is the hack -- we return the results on a cache-hit
if (smarty_core_read_cache_file($_params, $this)) {
return $_params['results'];
}
return false;
}

/**
* trigger SmartyBoot error
*
* @param string $error_msg
* @param integer $error_type
*/
function trigger_error($error_msg, $error_type = E_USER_WARNING)
{
trigger_error("SmartyBoot error: $error_msg", $error_type);
}
}
?>[/php:1:a4a346319b]
My eyeballs say that this is faster than what Smarty usually does for cache hits (10%??), though I still haven't decided if the overall impact is positive or not. I tried this with various caching alternatives (including andre's mmcache handler) and both with a php accelerator and without. It seems to always help, but especially with the php accelerator and mmcache handler.

YMMV


Last edited by boots on Wed Nov 12, 2003 2:29 am; edited 6 times in total
Back to top
View user's profile Send private message
boots
Administrator


Joined: 16 Apr 2003
Posts: 5611
Location: Toronto, Canada

PostPosted: Tue Nov 11, 2003 11:20 pm    Post subject: Reply with quote

Okay, I'll be surprised if there is any interest in this, but just in-case, here is a script you can use to autogenerate the above class from the Smarty sources and a custom template shown below.

SmartyBoot_creater.php
[php:1:534ec2a45e]<?php

/**
* SmartyBoot_creater.php
*
* creates the SmartyBoot.inc class from the Smarty.class.php sources
* and the SmartyBoot.class_tpl file
*
* @author boots
* @version 0.1.1 2003-NOV-19
* @since 2003-NOV-11
*/

#define ('SMARTY_DIR', '/path/to/smarty/libs/');
#define ('MY_APP_DIR', '/path/to/SmartyBoot.class_tpl');

$tpl_class = file_get_contents(MY_APP_DIR.'SmartyBoot.class_tpl');
$smarty_class = file_get_contents(SMARTY_DIR.'Smarty.class.php');

// get the header/defines
$got_header = preg_match('/<\?php(.*?)class Smarty/ms', $smarty_class, $m);
$tpl_class = str_replace('@@@SMARTY_DEFINES@@@', $m[1], $tpl_class);

// get the methods
$scrape_methods = array('_get_auto_id', '_get_auto_filename', '_read_file', '_parse_resource_name', '_fetch_resource_info', '_get_plugin_filepath');
$got_methods = preg_match_all('/(^\s{4}function\s(.*?)\(.*?^\s{4}\}\r)/ms', $smarty_class, $m);
$i = 0;
$methods = array();
foreach($m[2] as $method) {
if (in_array($method, $scrape_methods)) {
$methods[$method] = $m[1][$i];
}
$i++;
}

$tpl_class = str_replace('@@@SMARTY_METHODS@@@', join($methods), $tpl_class);

// get the vars
$got_vars = preg_match_all('/(^\s{4}var\s(.*?);\r)/ms', $smarty_class, $m);
$tpl_class = str_replace('@@@SMARTY_VARIABLES@@@', join($m[1]), $tpl_class);

file_put_contents_php4(MY_APP_DIR.'SmartyBoot.inc', $tpl_class);

function file_put_contents_php4 ($location, $whattowrite) {
if (file_exists($location)) {
unlink($location);
}
$fileHandler = fopen ($location, "w");
fwrite ($fileHandler, $whattowrite);
fclose ($fileHandler);
}
?>[/php:1:534ec2a45e]

It relies on the following "template" (non-smarty):

SmartyBoot.class_tpl
[php:1:534ec2a45e]<?php
/**
* SmartyBoot.inc
*
* Minimal Smarty configuration and cache lookup
*
* @author boots
* @version 0.1.0 2003-NOV-11
* @since 2003-OCT-27
*/

/**
* the following is scraped directly from Smarty.class.php
*/
@@@SMARTY_DEFINES@@@

class SmartyBoot
{
/**
* the following is scraped directly from Smarty.class.php
*/
@@@SMARTY_VARIABLES@@@

/**
* cache serials
*
* @var array
*/
var $_cache_serials = array();

/**
* info that makes up a cache file
*
* @var array
*/
var $_cache_info = array();

/**
* BEGIN CUSTOM CODE
*/

/**
* constructor
* @param object $smarty will be initialized only on cache-miss
* @param string $classname name of class to instantiate
* @param string $classpath path to class
* @param mixed $opts optional parameter to be passed to class constructor
* @param string $tpl_file name of template file
* @param string $cache_id
* @param string $compile_id
*/
function SmartyBoot(&$smarty, $classname='Smarty', $classpath=SMARTY_DIR, $opts='', $tpl_file='', $cache_id=null, $compile_id=null)
{
if (empty($tpl_file) || !($smarty=$this->is_cached($tpl_file, $cache_id, $compile_id))) {
$smarty = $this->get_smarty($classname, $classpath, $opts);
$this->configure_smarty($smarty);
}
}

/**
* Creates and returns a smarty instance
* @param string $classname name of class to instantiate
* @param string $classpath path to class
* @param mixed $opts optional parameter to be passed to class constructor
*/
function &get_smarty($classname='Smarty', $classpath=SMARTY_DIR, $opts='')
{
if ($classpath == SMARTY_DIR) {
$classpath .= 'Smarty.class.php';
}
include_once $classpath;
if ($opts=='') {
$smarty =& new $classname;
} else {
$smarty =& new $classname($opts);
}
return $smarty;
}

/**
* copy configuration to passed smarty instance
* @param object $smarty
*/
function configure_smarty(&$smarty)
{
foreach ((array)$this as $k=>$v) {
$smarty->$k = $v;
}
}

/**
* test to see if valid cache exists for this template
* note: this is a hacked version of Smarty is_cached()
*
* @param string $tpl_file name of template file
* @param string $cache_id
* @param string $compile_id
* @return string|false results of {@link _read_cache_file()}
*/
function is_cached($tpl_file, $cache_id = null, $compile_id = null)
{
if (!$this->caching)
return false;

if (!isset($compile_id))
$compile_id = $this->compile_id;

$_params = array(
'tpl_file' => $tpl_file,
'cache_id' => $cache_id,
'compile_id' => $compile_id
);
require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.read_cache_file.php');
// the following is the hack--it returns cache contents if there was a cache-hit
if (smarty_core_read_cache_file($_params, $this)) {
return $_params['results'];
}
return false;
}

/**
* trigger SmartyBoot error
*
* @param string $error_msg
* @param integer $error_type
*/
function trigger_error($error_msg, $error_type = E_USER_WARNING)
{
trigger_error("SmartyBoot error: $error_msg", $error_type);
}

/**
* END CUSTOM CODE
*/

/**
* the following is scraped directly from Smarty.class.php
*/

@@@SMARTY_METHODS@@@


/* vim: set expandtab: */

}
?>[/php:1:534ec2a45e]

Run the script and it will generate the SmartyBoot.inc script. I then suggest you create your own subclass as in the following example (don't bother using this example -- it is specific to my setup. You may like my method to set the "mode"):

LocalSmartyBoot.inc (example)
[php:1:534ec2a45e]<?php
/**
* LocalSmartyBoot.inc
*
* Minimal Smarty configuration and cache lookup
*
* @author boots
* @version 0.1.0 2003-NOV-11
* @since 2003-OCT-27
*/

require_once MY_APP_DIR . 'smarty_cache_mmcache.inc';
require_once MY_APP_DIR . 'SmartyBoot.inc';

define ('MY_TEMP_SMARTY_COMPILED_DIR', ROOT_DIR . 'tmp\\compiled');
define ('MY_TEMP_SMARTY_CACHED_DIR', ROOT_DIR . 'tmp\\cached');

class LocalSmartyBoot extends SmartyBoot {

function LocalSmartyBoot(&$smarty, $classname='Smarty', $classpath=SMARTY_DIR, $opts='', $tpl_file='', $cache_id=null, $compile_id=null)
{
if (!is_dir(MY_TEMP_SMARTY_COMPILED_DIR)) {
require_once BOS_DIR.'BOS_FS.class.inc';
BOS_FS::create_dir_structure(MY_TEMP_SMARTY_COMPILED_DIR);
}
if (!is_dir(MY_TEMP_SMARTY_CACHED_DIR)) {
require_once BOS_DIR.'BOS_FS.class.inc';
BOS_FS::create_dir_structure(MY_TEMP_SMARTY_CACHED_DIR);
}

$this->config_dir = MY_ROOT_DIR.'configs';
$this->template_dir = array(MY_ROOT_DIR.'templates', MY_ROOT_DIR.'data');
$this->compile_dir = MY_TEMP_SMARTY_COMPILED_DIR;
$this->plugins_dir = array(MY_ROOT_DIR.'plugins', SMARTY_DIR.'plugins');

$this->cache_dir = MY_TEMP_SMARTY_CACHED_DIR;
$this->cache_handler_func = 'smarty_cache_mmcache';

$this->default_resource_type = 'file';
$this->default_resource_naming = array('file'=>'.tpl');

//$this->use_sub_dirs = False;
//$this->security = true;

$this->cache_lifetime = 60;

if (array_key_exists('mode', $_REQUEST))
$mode = $_REQUEST['mode'];
$mode = (!empty($mode)) ? $mode : 'production';
$this->set_mode($mode);

SmartyBoot::SmartyBoot($smarty, $classname, $classpath, $opts, $tpl_file, $cache_id, $compile_id);

}

/**
* Sets the runtime mode
* @param string $mode 'debug', 'production', 'rebuild' or any combo separated by |
*/
function set_mode($mode='debug')
{
$this->mode = $mode;
foreach (explode("|", $mode) as $mode_type) {
switch ($mode_type) {
case 'debug':
// configure Smarty environment
$this->debugging = true;
$this->debugging_ctrl = 'SMARTY_DEBUG';
$this->debug_tpl = 'file:'.SMARTY_DIR.'debug.tpl';
break;
case 'rebuild':
// configure Smarty environment
$this->compile_check = true;
$this->force_compile = true;
$this->caching = false;
break;
case 'production':
// configure Smarty environment
$this->compile_check = true;
$this->force_compile = false;
$this->caching = true;
$this->debugging = false;
break;
}
}
}
}
?>[/php:1:534ec2a45e]

Then in the examples in my first post, just replace SmartyBoot with LocalSmartyBoot and you are golden.

Note that you can make an even leaner SmartyBoot class if you strip the comments in SmartyBoot.class_tpl and adjust the regex for $got_header to skip the opening comments from the Smarty class (which I preserved only to keep the proper copyright notices).

Tricky, yes? Have Fun Smile


Last edited by boots on Tue Dec 09, 2003 10:31 am; edited 1 time in total
Back to top
View user's profile Send private message
olli
Smarty n00b


Joined: 12 Nov 2003
Posts: 1

PostPosted: Tue Dec 09, 2003 10:09 am    Post subject: Failed opening required Reply with quote

Hi, I tried to run your script, it really sounds great!

but unfortunally I can't run the SmartyBoot_creater.php, because I need a BondDesk.inc file I can't find. Sad

(line 18: require_once 'localize/BondDesk.inc')

Where can I find this file?
Back to top
View user's profile Send private message
boots
Administrator


Joined: 16 Apr 2003
Posts: 5611
Location: Toronto, Canada

PostPosted: Tue Dec 09, 2003 10:29 am    Post subject: Reply with quote

You don't need that file. I should remove it from the listing. It is used to setup some of my custom paths and global settings is all. There are likely other changes you will have to make to get this to run on your environment. For example, I am not providing my custom BOS class, so you can omit the BOS::*() function calls. BUT, the LocalSmartyBoot file I listed is only an example-- you should use something appropriate to your smarty setup.

Also, this is really an experiment, but feel free to play Smile
Back to top
View user's profile Send private message
ephemerae
Smarty Rookie


Joined: 15 Feb 2004
Posts: 8
Location: kassel, germany

PostPosted: Sun Feb 15, 2004 1:02 pm    Post subject: Reply with quote

this sounds very clever, i like the idea! =)
have you done any tests / profiling to see if this technique really brings a significant performance gain?

i mean how big is the difference between your stripped down smarty class and the original one?
Back to top
View user's profile Send private message Visit poster's website
boots
Administrator


Joined: 16 Apr 2003
Posts: 5611
Location: Toronto, Canada

PostPosted: Mon Feb 16, 2004 6:20 am    Post subject: Reply with quote

hi ephemerae.

This was a cute experiment. The tests I've done show that it when it improves performance, the results are on average nominal. In many cases, there are no benefits and sometimes it is actually slower (for various reasons). Unfortunately, depending on what features you are using, this may break your site as certain features that would normally be loaded with the full Smarty will not be available during a cached page hit.

My idea behind this was driven by the want for a more modular Smarty that allowed features to be added piecewise as required. I wanted to learn how I might be able to approach such a problem and I did learn a few things doing this, but the general approach isn't practicle in my opinion.

Early this year Monte proposed a design that could be used to modularize Smarty and once I get back into the swing of things (I took the last two months off from programming) I'm going to persue that avenue instead.

Thanks for the comments!
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 -> Tips and Tricks 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