Optimisation de la mise en cache pour les templates Smarty

WRInaute impliqué
Bonjour,

Il y a depuis peu pas mal de nouveaux topics sur l'optimisation du chargement des pages, voici ma petite contribution.
La classe présentée, SmartyExtend, permet d'optimiser les performances des sites utilisant Smarty comme système de template. Cette classe gère la mise en cache des templates ainsi que l'ajout des entêtes d'expiration en accord avec le temps de mise en cache demandé.



La classe:

Code:
<?php

/**
 * Project:     SmartyExtend: permet d'améliorer la mise en cache des templates Smarty
 * File:        SmartyExtend.php
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * @author Nicolas Chevallier <nicolas.chevallier@b2b-net.com>
 * @version 1.0
 */


class SmartyExtend extends Smarty{
	
    public function __construct($tpl=null,$cache_time=0,$id=null)
    {
 		parent::__construct();

		// si on ne veut pas utiliser de cache
		if(is_null($tpl) || $cache_time == 0){
			$this->caching = 0;
		}
		else{
			// si on veut renouveller le fichier en cache
			if(isset($_GET['clearcache']) && $_GET['clearcache'] == 'true'){
				if($id!= null){
					$this->clear_cache ($tpl,$id);
				}else{
					$this->clear_cache($tpl);
				}
			}
			$this->caching = 2;
			$this->cache_lifetime = $cache_time;
			// Si le template est en cache, on l'affiche et on quitte
			if($id!= null){
				if($this->is_cached($tpl,$id)){
					$this -> display( $tpl,$id_original);
					exit(); 
				}
			}
			else{
				if($this->is_cached($tpl)){
					$this -> display( $tpl );
					exit(); 
				}			
			}
		}	
		//$this -> debugging = false;
		//$this -> debugging_ctrl = (DEBUG_MODE == true) ? 'URL' : 'NONE';
    }


	// surcharge de la fonction fetch pour améliorer les performances
	// gestion des headers Expires et Cache control
    function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false)
    {
    	if(isset($_GET['next'])){
			$cache_id = $cache_id.$_GET['next'];
		}
        static $_cache_info = array();
        
        $_smarty_old_error_level = $this->debugging ? error_reporting() : error_reporting(isset($this->error_reporting)
               ? $this->error_reporting : error_reporting() & ~E_NOTICE);

        if (!$this->debugging && $this->debugging_ctrl == 'URL') {
            $_query_string = $this->request_use_auto_globals ? $_SERVER['QUERY_STRING'] : $GLOBALS['HTTP_SERVER_VARS']['QUERY_STRING'];
            if (@strstr($_query_string, $this->_smarty_debug_id)) {
                if (@strstr($_query_string, $this->_smarty_debug_id . '=on')) {
                    // enable debugging for this browser session
                    @setcookie('SMARTY_DEBUG', true);
                    $this->debugging = true;
                } elseif (@strstr($_query_string, $this->_smarty_debug_id . '=off')) {
                    // disable debugging for this browser session
                    @setcookie('SMARTY_DEBUG', false);
                    $this->debugging = false;
                } else {
                    // enable debugging for this page
                    $this->debugging = true;
                }
            } else {
                $this->debugging = (bool)($this->request_use_auto_globals ? @$_COOKIE['SMARTY_DEBUG'] : @$GLOBALS['HTTP_COOKIE_VARS']['SMARTY_DEBUG']);
            }
        }

        if ($this->debugging) {
            // capture time for debugging info
            $_params = array();
            require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
            $_debug_start_time = smarty_core_get_microtime($_params, $this);
            $this->_smarty_debug_info[] = array('type'      => 'template',
                                                'filename'  => $resource_name,
                                                'depth'     => 0);
            $_included_tpls_idx = count($this->_smarty_debug_info) - 1;
        }

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

        $this->_compile_id = $compile_id;
        $this->_inclusion_depth = 0;

        if ($this->caching) {
            // save old cache_info, initialize cache_info
            array_push($_cache_info, $this->_cache_info);
            $this->_cache_info = array();
            $_params = array(
                'tpl_file' => $resource_name,
                'cache_id' => $cache_id,
                'compile_id' => $compile_id,
                'results' => null
            );
            require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
            if (smarty_core_read_cache_file($_params, $this)) {
                $_smarty_results = $_params['results'];
                if (!empty($this->_cache_info['insert_tags'])) {
                    $_params = array('plugins' => $this->_cache_info['insert_tags']);
                    require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
                    smarty_core_load_plugins($_params, $this);
                    $_params = array('results' => $_smarty_results);
                    require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php');
                    $_smarty_results = smarty_core_process_cached_inserts($_params, $this);
                }
                if (!empty($this->_cache_info['cache_serials'])) {
                    $_params = array('results' => $_smarty_results);
                    require_once(SMARTY_CORE_DIR . 'core.process_compiled_include.php');
                    $_smarty_results = smarty_core_process_compiled_include($_params, $this);
                }


                if ($display) {
                    if ($this->debugging)
                    {
                        // capture time for debugging info
                        $_params = array();
                        require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
                        $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = smarty_core_get_microtime($_params, $this) - $_debug_start_time;
                        require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php');
                        $_smarty_results .= smarty_core_display_debug_console($_params, $this);
                    }
                    if ($this->cache_modified_check) {
                        $_server_vars = ($this->request_use_auto_globals) ? $_SERVER : $GLOBALS['HTTP_SERVER_VARS'];
                        $_last_modified_date = @substr($_server_vars['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_server_vars['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3);
                        $_gmt_mtime = gmdate('D, d M Y H:i:s', $this->_cache_info['timestamp']).' GMT';
                        if (@count($this->_cache_info['insert_tags']) == 0
                            && !$this->_cache_serials
                            && $_gmt_mtime == $_last_modified_date) {
                            if (php_sapi_name()=='cgi')
                                header('Status: 304 Not Modified');
                            else
                                header('HTTP/1.1 304 Not Modified');
                        } else {
                        	// ajout de headers, le fichier est déja en cache
                        	// expires affecté à date de modification + temps de mise en cache
                        	header('Expires: ' . gmdate('D, d M Y H:i:s',$this->_cache_info['timestamp']+$this->cache_lifetime) . ' GMT');
							//header('Cache-Control: max-age='.$this->cache_lifetime - ($this->_cache_info['timestamp'] - time()));
                            header('Last-Modified: '.$_gmt_mtime);
                            echo $_smarty_results;
                        }
                    } else {
                            echo $_smarty_results;
                    }
                    error_reporting($_smarty_old_error_level);
                    // restore initial cache_info
                    $this->_cache_info = array_pop($_cache_info);
                    return true;
                } else {
                    error_reporting($_smarty_old_error_level);
                    // restore initial cache_info
                    $this->_cache_info = array_pop($_cache_info);
                    return $_smarty_results;
                }
            } // pas de fichier cache present
             else {
                $this->_cache_info['template'][$resource_name] = true;
                if ($this->cache_modified_check && $display) {
                    // ajout de headers, on vient de créer le fichier
                    // expires affecté now + à temps de mise en cache
                    header('Expires: ' . gmdate('D, d M Y H:i:s',time()+$this->cache_lifetime) . ' GMT');
					//header('Cache-Control: max-age='.$this->cache_lifetime);
                    header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
                }
            }
        }

        // load filters that are marked as autoload
        if (count($this->autoload_filters)) {
            foreach ($this->autoload_filters as $_filter_type => $_filters) {
                foreach ($_filters as $_filter) {
                    $this->load_filter($_filter_type, $_filter);
                }
            }
        }

        $_smarty_compile_path = $this->_get_compile_path($resource_name);

        // if we just need to display the results, don't perform output
        // buffering - for speed
        $_cache_including = $this->_cache_including;
        $this->_cache_including = false;
        if ($display && !$this->caching && count($this->_plugins['outputfilter']) == 0) {
            if ($this->_is_compiled($resource_name, $_smarty_compile_path)
                    || $this->_compile_resource($resource_name, $_smarty_compile_path))
            {
                include($_smarty_compile_path);
            }
        } else {
            ob_start();
            if ($this->_is_compiled($resource_name, $_smarty_compile_path)
                    || $this->_compile_resource($resource_name, $_smarty_compile_path))
            {
                include($_smarty_compile_path);
            }
            $_smarty_results = ob_get_contents();
            ob_end_clean();

            foreach ((array)$this->_plugins['outputfilter'] as $_output_filter) {
                $_smarty_results = call_user_func_array($_output_filter[0], array($_smarty_results, &$this));
            }
        }

        if ($this->caching) {
            $_params = array('tpl_file' => $resource_name,
                        'cache_id' => $cache_id,
                        'compile_id' => $compile_id,
                        'results' => $_smarty_results);
            require_once(SMARTY_CORE_DIR . 'core.write_cache_file.php');
            smarty_core_write_cache_file($_params, $this);
            require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php');
            $_smarty_results = smarty_core_process_cached_inserts($_params, $this);

            if ($this->_cache_serials) {
                // strip nocache-tags from output
                $_smarty_results = preg_replace('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!s'
                                                ,''
                                                ,$_smarty_results);
            }
            // restore initial cache_info
            $this->_cache_info = array_pop($_cache_info);
        }
        $this->_cache_including = $_cache_including;

        if ($display) {
            if (isset($_smarty_results)) { echo $_smarty_results; }
            if ($this->debugging) {
                // capture time for debugging info
                $_params = array();
                require_once(SMARTY_CORE_DIR . 'core.get_microtime.php');
                $this->_smarty_debug_info[$_included_tpls_idx]['exec_time'] = (smarty_core_get_microtime($_params, $this) - $_debug_start_time);
                require_once(SMARTY_CORE_DIR . 'core.display_debug_console.php');
                echo smarty_core_display_debug_console($_params, $this);
            }
            error_reporting($_smarty_old_error_level);
            return;
        } else {
            error_reporting($_smarty_old_error_level);
            if (isset($_smarty_results)) { return $_smarty_results; }
        }
    }
}//end of class
?>


Un exemple d'utilisation :
On prend l'exemple d'une fiche avec un paramètre id
Code:
<?php
// chargements des bibliothèques et inclusions...
$smarty = new SmartyExtend('template.tpl',24*60*60,$_GET["id"]);// mise en cache 24h

// Le code ci dessous ne sera éxécuté que si le template n'est pas en cache

// affichage et stockage du template une fois les traitements effectués
$content = $smarty -> fetch( 'template.tpl',$_GET["id"]); 
echo $content; 

?>

Dans cet exemple la page ne sera générée qu'une fois toutes les 24h, et la date d'expiration correspondra à la fin de la mise en cache. Cela permet à GoogleBot de connaître la date de prochain passage optimale, et aux visiteurs de garder en cache le fichier html (plus aucune requête ne sera nécessaire si les Etags sont désactivés). Pour regénérer une fiche il suffit d'ajouter ?clearcache=true à la fin de l'url.
 
Discussions similaires
Haut