Location: PHPKode > scripts > TemplateThis > TemplateThis.class.php
<?php

/**
 * TemplateThis - A PHP Templating Engine
 *
 * TemplateThis is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * TemplateThis 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 General Public License for more details.
 *
 * You should have recieved a copy of the GNU General Public License
 * along with TemplateThis; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * @package TemplateThis
 * @author Nicholas Sack <hide@address.com>
 * @link http://www.sourceforge.net/projects/ttc/ TemplateThis Project Page
 * @copyright Copyright (C) 2004, Nicholas Sack
 * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License
 * @version 0.2.8
 */

/* $Id: TemplateThis.class.php,v 1.5 2004/07/29 12:52:35 solias Exp $ */

/**
 * This sets the base path to the TemplateThis library directory
 * unless it has been already defined by the user. If this is not
 * defined, PHP will try and use its include_path.
 */
if (!defined('TT_DIR')) define('TT_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR);

/**
 * This define simple shortens PHPs built in global DIRECTORY_SEPARATOR.
 */
if (!defined('DIR_SEP')) define('DIR_SEP', DIRECTORY_SEPARATOR);

/**
 * @package TemplateThis
 */
class TemplateThis
{

    /**
     * Name of the directory where templates are stored.
     *
     * @var string $dir_template
     */
    var $dir_template = "templates";

    /**
     * Name of the directory where compiled templates are stored.
     *
     * @var string $dir_compiled
     */
    var $dir_compiled = "cache";

    /**
     * Name of the directory where plugins are kept.
     *
     * @var string $dir_plugin
     */
    var $dir_extension = "extensions";

    /**
     * Holds all of the template data.
     *
     * @var mixed $file_template
     */
    var $file_template;

    /**
     * Name of the compiled template filename.
     *
     * @see output()
     * @var string $file_compiled
     */
    var $file_compiled;

    /**
     * Name of that template that is being parsed.
     *
     * @see compile()
     * @var string $parser_template
     */
    var $parser_template;

    /**
     * Holds the last error messaged used.
     *
     * @see error()
     * @var string $last_error
     */
    var $last_error;

    /**
     * Filename of the template being parsed.
     *
     * @var string $template_path
     */
    var $template_path;

    /**
     * Array that holds all template data.
     *
     * @see output()
     * @var array $template_data
     */
    var $template_data = array();

    /**
     * Should we reuse code from the compiled template?
     *
     * @see output()
     * @var boolean $reuse_code
     */
    var $reuse_code = true;

    /**
     * Parser variable that holds tagged extensions.
     *
     * @see compile()
     * @var array $extension_tagged
     */
    var $extension_tagged = array();

    /**
     * Class constructor.
     *
     * @param string $template_file name of the template file
     */
    function TemplateThis($template_file)
    {
        if (!file_exists($template_file)) {
            exit($this->error("TemplateThis", "specified template does not exist"));
        } else {
            $this->template_path = $template_file;
        }
    }

    /**
     * Assigns specified value to tag name.
     *
     * @param mixed $tag_name
     * @param mixed $tag_value
     */
    function assign($tag_name, $tag_value)
    {
        if (is_array($tag_name)) {
            foreach ($tag_name as $s => $v) {
                $this->template_data[$s] = $v;
            }
        } else {
            $this->template_data[$tag_name] = $tag_value;
        }
    }

    /**
     * Appends a value to an existing tag.
     *
     * @param mixed $tag_name
     * @param mixed $tag_value
     */
    function append($tag_name, $tag_value)
    {
        if (is_array($tag_value)) {
            $this->template_data[$tag_name][] = $tag_value;
        } elseif (!is_array($this->template_data[$tag_name])) {
            $this->template_data[$tag_name] .= $tag_value;
        }
    }

    /**
     * Prepends a value to an existing tag.
     *
     * @param mixed $tag_name
     * @param mixed $tag_value
     */
    function prepend($tag_name, $tag_value)
    {
        if ((!is_array($tag_value)) && (!is_array($this->template_data[$tag_name]))) {
            $this->template_data[$tag_name] = $tag_value . $this->template_data[$tag_name];
        } else {
            exit($this->error("prepend", "arrays are not supported by this function"));
        }
    }

    /**
     * Parses the template and returns it in a variable array.
     *
     * @param mixed $_data optional
     * @return mixed
     */
    function result($_data = "")
    {
        ob_start();
        $this->output($_data);
        $result = ob_get_contents();
        ob_end_clean;
        return $result;
    }

    /**
     * Outputs the template to the web browser.
     *
     * @param string $_data optional
     */
    function output($_data = "")
    {
        global $_data;
        if (!is_array($_data)) {
            if (strlen($_data)) {
                $this->file_template = $_data;
            }
            $_data = &$this->template_data;
        }
        $_object = $_data;
        $_stack_count = 0;
        $_stack[$_stack_count++] = $_object;
        $this->file_compiled = $this->dir_compiled . DIR_SEP . preg_replace("/[:\/.\\\\]/", "_", $this->template_path) . ".php";
        $compile_template = true;
        if ($this->reuse_code) {
            if (is_file($this->file_compiled)) {
                if ($this->modified($this->file_compiled) > $this->modified($this->template_path)) {
                    $compile_template = false;
                }
            }
        }
        if ($compile_template) {
            if (!$this->compile($this->template_path)) {
                exit($this->error("output", "could not compile template"));
            }
        }
        include($this->file_compiled);
        unset($GLOBALS['_data']);
    }

    /**
     * Returns the modified time of the file.
     *
     * @param string $file
     * @return string
     */
    function modified($file)
    {
        if (is_file($file)) {
            return filemtime($file);
        } else {
            exit($this->error("modified", "specified file is not a file"));
        }
    }

    /**
     * Main compiler function of the engine.
     *
     * Depending on whether the {@link $dir_compiled} directory is
     * writable, this will either return the page for a boolean (if
     * it cannot write the file).
     *
     * @param string $template_file optional
     * @return mixed
     */
    function compile($template_file = "")
    {
        if (empty($template_file)) {
            $template_file = $this->template_path;
        }
        if ($file = fopen($template_file, "r")) {
            $this->parser_template = fread($file, filesize($template_file));
            fclose($file);
        } else {
            exit($this->error("compile", "could not open template file"));
        }
        $page = preg_replace("/<!-- ENDIF.+?-->/", "<?php\n    }\n?>", $this->parser_template);
        $page = preg_replace("/<!-- END[ a-zA-Z0-9_.]* -->/", "<?php\n        }\n        \$_object = \$_stack[--\$_stack_count];\n    }\n?>", $page);
        $page = str_replace("<!-- ELSE -->", "<?php\n    } else {\n?>", $page);
        if (preg_match_all("/<!-- BEGIN ([a-zA-Z0-9_.]+) -->/", $page, $var)) {
            foreach ($var[1] as $tag) {
                list($parent, $block) = $this->variable_name($tag);
                $code = "<?php\n"
                       ."    if (!empty(\$$parent" . "['$block'])) {\n"
                       ."        if (!is_array(\$$parent" . "['$block'])) {\n"
                       ."            \$$parent" . "['$block'] = array(array('block' => \$$parent" . "['$block']));\n"
                       ."        }\n"
                       ."        \$_tmp_keys = array_keys(\$$parent" . "['$block']);\n"
                       ."        if (\$_tmp_keys[0] != \"0\") {\n"
                       ."            \$$parent" . "['$block'] = array(0 => \$$parent" . "['$block']);\n"
                       ."        }\n"
                       ."        \$_stack[\$_stack_count++] = \$_object;\n"
                       ."        foreach (\$$parent" . "['$block'] as \$r_count => \$$block) {\n"
                       ."            \$_object = &\$$block;\n?>";
                $page = str_replace("<!-- BEGIN $tag -->", $code, $page);
            }
        }
        if (preg_match_all("/<!-- (ELSE)?IF ([a-zA-Z0-9_.]+)([!=<>]+)\"([^\"]*)\" -->/", $page, $var)) {
            foreach ($var[2] as $count => $tag) {
                list($parent, $block) = $this->variable_name($tag);
                $cmp = $var[3][$count];
                $val = $var[4][$count];
                $else = ($var[1][$count] == "ELSE") ? "} else" : "";
                if ($cmp == "=") {
                    $cmp = "==";
                }
                $code = "<?php\n    $else" . "if (\$$parent" . "['$block'] $cmp \"$val\") {\n?>";
                $page = str_replace($var[0][$count], $code, $page);
            }
        }
        if (preg_match_all("/<!-- (ELSE)?IF ([a-zA-Z0-9_.]+) -->/", $page, $var)) {
            foreach ($var[2] as $count => $tag) {
                $else = ($var[1][$count] == "ELSE") ? "} else" : "";
                list($parent, $block) = $this->variable_name($tag);
                $code = "<?php\n    $else" . "if (!empty(\$$parent" . "['$block'])) {\n?>";
                $page = str_replace($var[0][$count], $code, $page);
            }
        }
        if (preg_match_all("/{([a-zA-Z0-9_. >]+)}/", $page, $var)) {
            foreach ($var[1] as $fulltag) {
                list($command, $tag) = $this->command_name($fulltag);
                list($block, $scalar) = $this->variable_name($tag);
                $code = "<?php $command \$$block" . "['$scalar']; ?>";
                $page = str_replace("{" . $fulltag . "}", $code, $page);
            }
        }
        if (preg_match_all("/<\"([a-zA-Z0-9_.]+)\">/", $page, $var)) {
            foreach ($var[1] as $tag) {
                list($block, $scalar) = $this->variable_name($tag);
                $code = "<?php echo gettext('$scalar'); ?>";
                $page = str_replace("<\"" . $tag . "\">", $code, $page);
            }
        }
        if (preg_match_all("/{([a-zA-Z0-9_]+):([^}]*)}/", $page, $var)) {
            foreach ($var[2] as $count => $tag) {
                list($command, $tag) = $this->command_name($tag);
                $extension = $var[1][$count];
                if (!$this->extension_tagged[$extension]) {
                    $header .= "<?php\n    include_once(\"" . TT_DIR . $this->dir_extension . DIR_SEP . "extension_$extension.php\");\n?>\n";
                    $this->extension_tagged[$extension] = true;
                }
                if (!strlen($tag)) {
                    $code = "<?php $command extension_$extension(); ?>";
                } elseif (substr($tag, 0, 1) == "\"") {
                    $code = "<?php $command extension_$extension($tag); ?>";
                } elseif (strpos($tag, ",")) {
                    list($tag, $addparam) = explode(",", $tag, 2);
                    list($block, $scalar) = $this->variable_name($tag);
                    if (preg_match("/^([a-zA-Z_]+)/", $addparam, $match)) {
                        $next_tag = $match[1];
                        list($next_block, $next_scalar) = $this->variable_name($next_tag);
                        $addparam = substr($addparam, strlen($next_tag));
                        $code = "<?php $command extension_$extension(\$$block" . "['$scalar'], \$$next_block" . "['$next_scalar']" . "$addparam); ?>";
                    } else {
                        $code = "<?php $command extension_$extension(\$$block" . "['$scalar'], $addparam); ?>";
                    }
                } else {
                    list($block, $scalar) = $this->variable_name($tag);
                    $code = "<?php $command extension_$extension(\$$block" . "['$scalar']); ?>";
                }
                $page = str_replace($var[0][$count], $code, $page);
            }
        }
        if (isset($header)) {
            $page = "$header\n$page";
        }
        if (strlen($this->file_compiled)) {
            if ($file = fopen($this->file_compiled, "w")) {
                fwrite($file, $page);
                fclose($file);
                return true;
            } else {
                $this->error("compile", "could not write compiled file");
                return false;
            }
        } else {
            return $page;
        }
    }

    /**
     * Names a specified tag using a stack and returns it.
     *
     * @param mixed $tag
     * @return array
     */
    function variable_name($tag)
    {
        $p_level = 0;
        while (substr($tag, 0, 7) == "parent.") {
            $tag = substr($tag, 7);
            $p_level++;
        }
        if (substr($tag, 0, 4) == "top.") {
            $object = "_stack[0]";
            $tag = substr($tag, 4);
        } elseif ($p_level) {
            $object = "_stack[$stack_count-" . $p_level . "]";
        } else {
            $object = "_object";
        }
        while (is_int(strpos($tag, "."))) {
            list($parent, $tag) = explode(".", $tag, 2);
            if (is_numeric($parent)) {
                $object .= "[" . $parent . "]";
            } else {
                $object .= "['" . $parent . "']";
            }
        }
        return array($object, $tag);
    }

    /**
     * Names a command from a tag and returns it.
     *
     * @param mixed $tag
     */
    function command_name($tag)
    {
        if (preg_match("/^(.+) > ([a-zA-Z0-9_.]+)$/", $tag, $tag_var)) {
            $tag = $tag_var[1];
            list($new_block, $new_scalar) = $this->variable_name($tag_var[2]);
            $command = "\$$new_block" . "['$new_scalar'] = ";
        } else {
            $command = "echo";
        }
        return array($command, $tag);
    }

    /**
     * Generates an error and sends it to the browser.
     *
     * @todo a better error reporting system
     * @param string $function name of the function that has the error
     * @param string $error error description
     */
    function error($function, $error)
    {
        $this->last_error = "<b>" . $function . ":</b> " . $error;
        print $this->last_error;
    }
}
Return current item: TemplateThis