Location: PHPKode > projects > phlyMail Lite > phlymail/shared/lib/fxl_template.inc.php
<?php
/**
 * fxl_template
 *
 * a very tiny but flexible library for template processing
 *
 * Have a look into the examples directory
 * for some useful examples incl. template files.
 *
 * Goals of this library:
 *
 * - plain text/html templates without any control mechanisms
 *   like loops, php code or sql queries
 * - easy to learn template markup (only 2 elements)
 * - flexibility: you can more or less assign everything to everywhere
 * - speed
 * - easy handling: It's just this tiny file you have to include
 *   to use fxl template
 *
 * @version 2.1.1
 * @version 2.1.1mod1 2010-10-11 by <hide@address.com>
 * @package fxl_template
 */

class fxl_template {

    protected $tpl = array('block' => array(), 'place' => array(), 'template' => '');
    protected $param = array('clipleft' => '{', 'clipright' => '}');
    protected $halt_on_error = true;
    protected $template_file = '';

   /**
    * fxl_template constructor
    *
    * example:
    * <code>
    * $tpl = new fxl_template('template.tpl');
    * </code>
    *
    * @param string $content file name
    * @param array $options not in use
    * @return object fxl_template
    */
    public function __construct ($content = false, $options = false)
    {
        if (!file_exists($content) || !is_readable($content)) die('Cannot open template file '.$content);
        $this->template_file = $content;
        $this->set_template($content, 'file');
        if ($this->tpl['template']) $this->init();
    }

    /**
     * Allows to extent the $param property by custom keys.
     *
     * @param string|array $key  Array of key => value pairs or scalar param name
     *[@param mixed $value  If $key is a string, this holds the value, otherwise leave it NULL]
     */
    public function set_custom_param($key, $value = null)
    {
        if (!is_null($value) && is_scalar($key)) {
            $key = array($key => $value);
        }
        foreach ($key as $k => $v) {
            if ($k == 'clipleft' || $k == 'clipright') continue;
            $this->param[$k] = $v;
        }
    }

    /**
     * Retrieve the value of a custom param
     *
     * @param string $name  Name of the param to retrieve
     * @return mixed  NULL, if param could not be found, the value of it otherwise
     */
    public function get_custom_param($name)
    {
        return (isset($this->param[$name])) ? $this->param[$name] : null;
    }

   /**
    * finish rendering process
    *
    * @since 1.0.0
    * @return string rendered template
    */
    public function get_content()
    {
        $tmp = preg_replace('/'.$this->param['clipleft'].'([a-z0-9\-\_]+)'.$this->param['clipright'].'/i', '', $this->get());
        return preg_replace('/'.$this->param['clipleft'].'\\\/i', $this->param['clipleft'], $tmp);
    }

   /**
    * displays the output
    *
    * same as:
    * <code>
    * echo $tpl->get_content();
    * </code>
    * @since 1.0.0
    * @return void
    */
    public function display()
    {
        echo $this->get_content();
    }

   /**
    * new assignment
    *
    * complete example:
    * <code>
    * $tpl = new fxl_template('address_form.tpl');
    * $tpl_address = $tpl->get_block('address');
    * $tpl_address->assign('name', 'Peter');
    * $tpl_address->assign(array('zip' => '10179', 'town' => 'Berlin'));
    * $tpl->assign('address', $tpl_address);
    * $tpl->display();
    * </code>
    *
    * usage examples:
    * <code>
    * $tpl->assign(<string var>, <string val>);
    * $tpl->assign(<string var>, <object fxl_template>);
    * $tpl->assign(<string blockname>, <object fxl_template>);
    * $tpl->assign(<array [var=val,var=val]>);
    * $tpl->assign(<string blockname>); *
    * $tpl->assign(<string blockname>, val);
    * </code>
    *
    * * since version 2.1.1
    *
    * @since 1.0.0
    *
    * @param string|array $var string: name of the block / place holder OR array with key/value pairs
    * @param string|object $val string OR object (another FXL Template object)
    * @return void
    */
    public function assign($var, $val = null)
    {
        if (is_array($var)) {
            foreach ($var as $k => $v) {
                $this->tpl['place'][$k][] = $v;
            }
        } elseif (is_object($val)) {
            $this->tpl['place'][$var][] = clone $val;
        } elseif (strlen($var)) {
            if (is_null($val)) {
                $this->assign_block($var);
            } else {
                $this->tpl['place'][$var][] = $val;
            }
        }
    }

   /**
    * assigns a whole block in place
    *
    * <code>
    * $tpl->assign_block('blockname');
    *
    * same as:
    * $tpl_block = $tpl->get_block('blockname');
    * $tpl->assign('blockname', $tpl_block);
    *
    * same as:
    * $tpl->assign('blockname');
    * </code>
    *
    * @param string $blockname block name
    * @return void
    */
    public function assign_block($blockname)
    {
        $this->assign($blockname, $this->get_block($blockname));
    }

    /**
     * Wrapper for getting a block, assigining it one or more placeholders and then
     * assigning the now filled block to its parent template again.
     * @param string $blk Name of the block
     * @param mixed $var See $this->assign()
     * @param mixed $val See $this->assign()
     * @since 2.0.5
     */
    public function fill_block($blk, $var, $val = '')
    {
        $b = $this->get_block($blk);
        $b->assign($var, $val);
        $this->assign($blk, $b);
    }

   /**
    * fetching a block for assignments
    *
    * @since 1.0.0
    * @param string $blockname block name
    * @return fxl_template
    */
    public function get_block($blockname)
    {
        if (isset($this->tpl['block'][$blockname]) && is_object($this->tpl['block'][$blockname])) {
            return clone $this->tpl['block'][$blockname];
        } elseif ($this->halt_on_error) {
            die('Block: '.$blockname.' not found in '.$this->template_file);
        }
        return false;
    }

   /**
    * checks a block exists or not
    *
    * @since 2.0.0
    * @param string $blockname block name
    * @return bool
    */
    public function block_exists($blockname)
    {
        if (isset($this->tpl['block'][$blockname]) && is_object($this->tpl['block'][$blockname])) {
            return true;
        }
        return false;
    }

   /**
    * refresh block for new assignments
    *
    * example:
    * <code>
    * $names = array('peter', 'nicole');
    * $tpl_name = $tpl->get_block('name_block');
    * foreach ($names as $name) {
    *     $tpl_name->assign('name', $name);
    *     $tpl->assign('name_block', $tpl_name);
    *     $tpl_name->clear();
    * }
    * </code>
    *
    * @since 1.0.0
    * @return void
    */
    public function clear()
    {
        $this->tpl['place'] = array();
    }

    /**
     * pre-rendered content - not all replacements done
     *
     * @ignore
     * @return string pre-rendered template
     */
    public function get()
    {
        if (count($this->tpl['place'])) {
            foreach ($this->tpl['place'] as $k => $v) {
                $replace = '';
                for ($i = 0, $j = count($this->tpl['place'][$k]); $i < $j; $i++) {
                    $replace .= is_object($this->tpl['place'][$k][$i]) ? $this->tpl['place'][$k][$i]->get() : $this->tpl['place'][$k][$i];
                }
                $this->tpl['template'] = str_replace($this->param['clipleft'].$k.$this->param['clipright'], $replace, $this->tpl['template']);
            }
        }
        return $this->tpl['template'];
    }

    # INTERNAL METHODS #

   /**
    * sets the template
    *
    * @since 2.0.0
    * @ignore
    * @param string $data enum: file name, content string
    * @param string $type enum: file, string
    * @return bool
    */
    public function set_template($data, $type = 'file')
    {
        if ($type == 'file') {
            if (($this->tpl['template'] = file_get_contents($data))) return true;
        } elseif ($type == 'string') {
            $this->tpl['template'] = $data;
            return true;
        }
        return false;
    }

   /**
    * template initialization
    *
    * @ignore
    * @since 2.0.0
    * @return void
    */
    public function init()
    {
    	return $this->parse($this->tpl['template']);
    }

    /**
     * parser
     *
     * @ignore
     * @param string $tplstring
     * @return void
     */
    protected function parse($tplstring = '')
    {
        $this->tpl['template'] = $tplstring;
        $m = $this->_match_block();
        for ($x = 0, $y = count($m[0]); $x < $y; $x++) {
        	$this->tpl['template'] = $this->parse_block($m[1][$x], $this->tpl['template']);
        	$this->tpl['block'][$m[1][$x]] = clone $this;
            $this->tpl['block'][$m[1][$x]]->tpl['place'] = array();
            $this->tpl['block'][$m[1][$x]]->tpl['block'] = array();
            $this->tpl['block'][$m[1][$x]]->parse($m[2][$x]);
        }
    }

    /**
     * block replacer
     *
     * @ignore
     * @param string $blockname
     * @param string $tplstring
     * @return string
     */
    protected function parse_block($blockname = '', $template = '')
    {
        $blockname = preg_quote($blockname);
        return preg_replace
                ('/<!--\sSTART\s('.$blockname.')\s-->.*<!--\sEND\s('.$blockname.')\s-->/ms'
                ,$this->param['clipleft'].$blockname.$this->param['clipright']
                ,$template
                );
    }

    /**
     * block finder
     *
     * @ignore
     * @return array matches
     */
    protected function _match_block()
    {
        preg_match_all("/<!--\sSTART\s([a-z0-9_]+)\s-->(.*)<!--\sEND\s(\\1)\s-->/mis", $this->tpl['template'], $m);
        return $m;
    }
}


/**
 * FXL Template - Memcache Extension (alpha, 0.5)
 *
 * for use with FXL Template v2.1+
 *
 * php memcache documentation:
 * http://www.php.net/manual/en/book.memcache.php
 *
 * @package fxl_template
 */

class fxl_memcached_template extends fxl_template {
    protected $check = 'always';
    protected $validate = 'date';
    protected $memcache_prefix = 'fxlt';
    protected $memcache_flag = null;
    protected $memcache_expire = null;
    public $cached = false;
    protected $cache_md5;
    protected $cache_date;

    /**
     * Constructor
     *
     * possible options:
     * - check
     * value: 'never' never check for a new version of the template (fastest)
     * value: 'always' always check for a newer version of the template (default, recommended)
     * - validate
     * value: 'date' (fastest)
     * value: 'md5' (default, recommended)
     *
     * @param string $filename template filename
     * @param Memcache $memcache Memcache object
     * @param string $check (never|always) optional
     * @param string $validate (date|md5) optional
     * @param array $mc_option (0=>key, 1=>flag, 2=>expire) optional
     */
    function __construct($filename, $memcache, $check = null, $validate = null, $mc_option = null) {
        if (in_array($check, array('always', 'never'))) $this->check = $check;
        if (in_array($validate, array('date', 'md5'))) $this->validate = $validate;
        if (isset($mc_option[1])) $this->memcache_flag = $mc_option[1];
        if (isset($mc_option[2])) $this->memcache_expire = $mc_option[2];
        if (!file_exists($filename) || !is_readable($filename)) return false;
        $key = (isset($mc_option[0]) && !is_null($mc_option[0])) ? $mc_option[0] : $this->memcache_prefix.realpath($filename);
        if ($memcache->get($key)) {
            $data = $memcache->get($key);
            if ($this->check == 'always') {
                if ($this->validate == 'md5') {
                    $content = file_get_contents($filename);
                    if (md5($content) == $data->cache_md5) {
                        $this->tpl = $data->tpl;
                    }
                } elseif ($data->cache_date == filemtime($filename)) {
                    $this->tpl = $data->tpl;
                }
            } else {
                $this->tpl = $data->tpl;
            }
        }
        if (!$this->tpl['template']) {
            $content = file_get_contents($filename);
            $this->set_template($content, 'string');
            $this->init();
            $this->cache_md5 = md5($content);
            $this->cache_date = filemtime($filename);
            $memcache->set($key, $this, $this->memcache_flag, $this->memcache_expire);
        } else {
            $this->cached = true;
        }
    }

}

/**
 * FXL TEMPLATE CACHE PLUGIN: ser v1.0.0
 * base class for the caching implementation
 */
class fxl_ser_template extends fxl_template
{
    protected $mode = '';
    protected $check = true;
    protected $force = false;
    protected $cache_suffix = '.cache';
    protected $cache_prefix = '';
    protected $halt_on_error = false;
    protected $cache_file = '';
    protected $template_file = '';
    protected $version = 1.0;
    protected $sub = false;

    public function __construct($template_file = '')
    {
        if ($template_file && !$this->set_template_file($template_file)) return false;
    }

    public function set_check($val)
    {
        $this->check = (bool) $val;
        return true;
    }

    public function set_mode($val)
    {
        if (in_array($val, array('md5'))) {
            $this->mode = $val;
            return true;
        }
        return false;
    }

    public function set_template($data, $type = 'file')
    {
        if ($type == 'file') {
            if (!file_exists($data) || !is_readable($data)) die('Cannot open template file '.$data);
            $this->tpl['template'] = file_get_contents($data);
            $this->template_file = $data;
            return true;
        }
        if ($type == 'string') return (bool) $this->tpl['template'] = $data;
        return false;
    }

    public function get_cache_file_name($template_file = '')
    {
        if ($template_file && ($this->cache_prefix || $this->cache_suffix)) {
            return $this->cache_prefix.$this->template_file.$this->cache_suffix;
        } elseif ($this->cache_file) {
            return $this->cache_file;
        } elseif ($this->template_file && ($this->cache_prefix || $this->cache_suffix)) {
            return $this->cache_prefix.$this->template_file.$this->cache_suffix;
        }
        return false;
    }

    public function set_cache_file($filename)
    {
        return (bool) $this->cache_file = $filename;
    }

    public function init()
    {
        if (!$this->tpl['template']) return false;
        if (!$cfile = $this->get_cache_file_name()) return false;

        if ($this->check && file_exists($cfile) && is_readable($cfile)) {
            $fp = fopen($cfile, 'r');
            $header_line = fgets($fp, 256);
            $header = explode(':', $header_line, 2);
            if (!isset($header[1]) || (chop($header[1]) != md5($this->tpl['template']))) $this->force = true;
            if (!$this->force) $ser = fread($fp, filesize($cfile) - strlen($header_line));
            fclose($fp);
        } elseif ($this->check && (!file_exists($cfile))) {
            $this->force = true;
        }
        if ($this->force) {
        	$head = 'md5:'.md5($this->tpl['template'])."\n";
            $this->parse($this->tpl['template']);
            $cached = serialize($this);
            file_put_contents($cfile, $head.$cached);
        } else {
            $cached = unserialize($ser);
            $this->tpl = $cached->tpl;
        }
    }

    protected function md5_check($val1, $val2)
    {
        return ($val1 == md5($val2));
    }

    protected function __clone()
    {
        $this->cache_file = ''; // cannot be the same
        $this->template_file = ''; // cannot be the same
        $this->sub = true;
    }

    public function version() { return $this->version; }
}

/**
 *  FXL CACHED TEMPLATE WRAPPER
 *
 *  working cache plugin example for fxl_template based on md5 and php serialize
 *  Feel free to customize it for your needs ;-)  Just use:
 *
 *  $tpl = new fxl_cached_template($tpl_file, $cache_file);
 *
 *  instead of:
 *
 *  $tpl = new fxl_template($tpl_file);
 *
 *  btw, you could also use:
 *  $tpl = new fxl_template($tpl_file, array('cache_file' => 'tpl.cache', 'cache_mode' => 'ser'));
 */
class fxl_cached_template extends fxl_ser_template
{
    /**
     * fxl_cached_template constructor
     *
     * make sure you have write permissions for your cache dir / cache file
     *
     * @param string $tpl template file
     * @param string $ctpl cached template file
     * @return object fxl_cached_template
     */
    function __construct($tpl, $ctpl = false)
    {
        if (!$ctpl) $ctpl = $tpl.'.cache';
        $this->set_template($tpl);
        $this->set_cache_file($ctpl);
        $this->set_mode('ser');
        $this->init();
    }
}

/**
 * phlyMail specifics for using the FXL Template Engine
 * It modifies the way, the caching is handled and automagically assigns
 * translation values according to their prefix as HTML safe or Javascript safe
 * values.
 */
class phm_template extends fxl_template
{
    var $msg = null;

    /**
     * Constructor
     *
     * @param string $filename  Full path to the template file
     * @param string $cachepath Base path where to cache the file
     * @param array $msg  Localization strings, which get assigned to the template
     */
    public function __construct($filename, $cachepath, $msg = null)
    {
        foreach ($msg as $k => $v) { // Flatten array messages into scalars
            if (is_array($v)) {
                foreach ($v as $k2 => $v2) $msg[$k.'_'.$k2] = $v2;
            }
        }
        $this->msg = $msg;
        $cachefile = $cachepath.basename($filename);
        parent::__construct($filename, array('cache_file' => $cachefile, 'cache_mode' => 'ser'));
    }

    /**
     * Variation of the original method: Replaces localization strings on the spot
     *
     * @param mixed $data  Either a file name reference or a string
     * @param string $type  Denotes the type of $data
     * @return bool
     */
    public function set_template($data, $type = 'file')
    {
        if ($type == 'file') {
            $tplstr = file_get_contents($data);
            if (!is_null($this->msg)) {
                $tplstr = preg_replace_callback("/%([hjt])([a-z0-9\_]+)%/", array(&$this, 'localize'), $tplstr);
            }
            if (($this->tpl['template'] = $tplstr)) return true;
        } elseif ($type == 'string') {
            $this->tpl['template'] = $data;
            return true;
        }
        return false;
    }

    public function localize($array)
    {
        if (isset($this->msg[$array[2]])) {
            if ($array[1] == 'h') return htmlentities($this->msg[$array[2]], null, 'utf-8');
            if ($array[1] == 'j') return addslashes($this->msg[$array[2]]);
        }
        return false;
    }
}
?>
Return current item: phlyMail Lite