<?PHP
/**
 * patTemplate
 *
 * $Id: patTemplate.php,v 1.94.4.7 2004/12/31 14:01:46 schst Exp $
 *
 * powerful templating engine
 *
 * @version        3.0.0
 * @package        patTemplate
 * @author        Stephan Schmidt <schst@php.net>
 * @license        LGPL
 * @link        http://www.php-tools.net
 */

/**
 * template already exists
 */
define'PATTEMPLATE_ERROR_TEMPLATE_EXISTS'5010 );

/**
 * template does not exist
 */
define 'PATTEMPLATE_WARNING_NO_TEMPLATE'5011 );

/**
 * unknown type
 */
define 'PATTEMPLATE_WARNING_UNKNOWN_TYPE'5012 );

/**
 * base class for module could not be found
 */
define'PATTEMPLATE_ERROR_BASECLASS_NOT_FOUND'5050 );

/**
 * module could not be found
 */
define'PATTEMPLATE_ERROR_MODULE_NOT_FOUND'5051 );

/**
 * array expected
 */
define'PATTEMPLATE_ERROR_EXPECTED_ARRAY'5052 );

/**
 * No input
 */
define'PATTEMPLATE_ERROR_NO_INPUT'6000 );

/**
 * patTemplate
 *
 * powerful templating engine
 *
 * @version        3.0.0
 * @package        patTemplate
 * @author        Stephan Schmidt <schst@php.net>
 * @license        LGPL
 * @link        http://www.php-tools.net
 */
class patTemplate
{
   
/**
    * standard system vars that identify pat tools
    * @var    array
    */
    
var    $_systemVars            =    array(
                                        
'appName'        =>    'patTemplate',
                                        
'appVersion'    =>    '3.0.0',
                                        
'author'        =>    array(
                                                                    
'Stephan Schmidt <schst@php.net>'
                                                                 
)
                                    );

   
/**
    * default attributes for new templates
    * @access    private
    * @var        array
    */
    
var    $_defaultAttributes    =    array(
                                        
'type'            =>    'standard',
                                        
'visibility'    =>    'visible',
                                        
'loop'            =>    1,
                                        
'unusedvars'    =>    'strip',
                                        
'whitespace'    =>    'keep',
                                        
'autoclear'        =>    'off',
                                        
'autoload'        =>    'on'
                                    
);

   
/**
    * options for patTemplate
    *
    * Currently the following options are implemented:
    * - maintainBc defines, whether patTemplate should be backwards compatible.
    *   This means, that you may use 'default' and 'empty' for subtemplates.
    *
    * @access    private
    * @var        array
    */
    
var    $_options    =    array(
                                
'startTag'   => '{',
                                
'endTag'     => '}',
                                
'root'       => '.',
                                
'namespace'  => 'patTemplate',
                                
'maintainBc' => true
                             
);

   
/**
    * start tag
    *
    * @access    private
    * @var        string
    */
    
var $_startTag '{';

   
/**
    * end tag
    *
    * @access    private
    * @var        string
    */
    
var $_endTag '}';

   
/**
    * loaded modules
    *
    * Modules are:
    * - Readers
    * - Caches
    * - Variable modifiers
    * - Filters
    *
    * @access    private
    * @var        array
    */    
    
var    $_modules        =    array();
    
   
/**
    * directories, where modules can be stored
    * @access    private
    * @var        array
    */
    
var    $_moduleDirs    =    array();

   
/**
    * stores all template names
    * @access    private
    * @var        array
    */
    
var    $_templateList    =    array();

   
/**
    * stores all template data
    * @access    private
    * @var        array
    */
    
var    $_templates        =    array();
    
   
/**
    * stores all global variables
    * @access    private
    * @var        array
    */
    
var    $_globals    =    array();

   
/**
    * stores all local variables
    * @access    private
    * @var        array
    */
    
var    $_vars    =    array();
    
   
/**
    * stores the name of the first template that has been
    * found
    *
    * @access    private
    * @var        string
    */
    
var    $_root;

   
/**
    * output filters that should be used
    *
    * @access    private
    * @var        array
    */
    
var    $_outputFilters = array();

   
/**
    * input filters that should be used
    *
    * @access    private
    * @var        array
    */
    
var    $_inputFilters = array();

   
/**
    * template cache, that should be used
    *
    * @access    private
    * @var        array
    */
    
var    $_tmplCache null;

   
/**
    * Create a new patTemplate instance.
    *
    * The constructor accepts the type of the templates as sole parameter.
    * You may choose one of:
    * - html (default)
    * - tex
    *
    * The type influences the tags you are using in your templates.
    *
    * @access    public
    * @param    string    type (either html or tex)
    */
    
function patTemplate$type 'html' )
    {
        if( !
defined'PATTEMPLATE_INCLUDE_PATH' ) )
            
define'PATTEMPLATE_INCLUDE_PATH'dirname__FILE__ ) . '/patTemplate' );

        
$this->setType$type );
    }
    
   
/**
    * sets an option
    *
    * Currently, the following options are supported
    * - maintainBc (true|false)
    * - namespace (string)
    *
    * @access    public
    * @param    string    option to set
    * @param    string    value of the option
    */
    
function setOption$option$value )
    {
        
$this->_options[$option] = $value;
    }

   
/**
    * gets an option
    *
    * @access    public
    * @param    string    option to get
    * @return    mixed    value of the option
    */
    
function getOption$option )
    {
        if( !isset( 
$this->_options[$option] ) )
            return 
null;
        return 
$this->_options[$option];
    }

   
/**
    * sets name of directory where templates are stored
    *
    * @access    public
    * @param    string    dir where templates are stored
    * @deprecated        please use patTemplate::setRoot() instead
    */
    
function setBasedir$basedir )
    {
        
$this->_options['root']    =    $basedir;
    }

   
/**
    * sets root base for the template
    *
    * The parameter depends on the reader you are using.
    *
    * @access    public
    * @param    string    root base of the templates
    */
    
function setRoot$root )
    {
        
$this->_options['root']    =    $root;
    }

   
/**
    * gets name of root base for the templates
    *
    * @access    public
    * @return    mixed         root base
    */
    
function getRoot()
    {
        return    
$this->_options['root'];
    }

   
/**
    * sets namespace of patTemplate tags
    *
    * @access    public
    * @param    string    namespace
    */
    
function setNamespace$ns )
    {
        
$this->_options['namespace']    =    $ns;
    }

   
/**
    * gets namespace of patTemplate tags
    *
    * @access    public
    * @return    string    namespace
    */
    
function getNamespace()
    {
        return    
$this->_options['namespace'];
    }

   
/**
    * set default attribute
    *
    * @access    public
    * @param    string    attribute name
    * @param    mixed    attribute value
    */
    
function setDefaultAttribute$name$value )
    {
        
$this->_defaultAttributes[$name]    =    $value;
    }

   
/**
    * set default attributes
    *
    * @access    public
    * @param    array    attributes
    */
    
function setDefaultAttributes$attributes )
    {
        
$this->_defaultAttributes    =    array_merge$this->_defaultAttributes$attributes );
    }

   
/**
    * get default attributes
    *
    * @access    public
    * @return    return default attributes
    */
    
function getDefaultAttributes()
    {
        return    
$this->_defaultAttributes;
    }

   
/**
    * set the type for the templates
    *
    * @access    public
    * @param    string    type (html or tex)
    * @return    boolean    true on success
    */
    
function setType$type )
    {
        switch( 
strtolower$type ) )
        {
            case 
"tex":
                
$this->setTags'<{''}>' );
                break; 
            case 
"html":
                
$this->setTags'{''}' );
                break;
            default:
                return    
patErrorManager::raiseWarning(
                                                        
PATTEMPLATE_WARNING_UNKNOWN_TYPE,
                                                        
"Unknown type '$type'. Please use 'html' or 'tex'."
                                                    
);
        }
        return 
true;
    }
    
   
/**
    * set the start and end tag for variables
    *
    * @access    public
    * @param    string    start tag
    * @param    string    end tag
    * @return    boolean    true on success
    */
    
function setTags$startTag$endTag )
    {
        
$this->_options['startTag']    =    $startTag;
        
$this->_options['endTag']    =    $endTag;

        
$this->_startTag    =    $startTag;
        
$this->_endTag        =    $endTag;
        return 
true;
    }

   
/**
    * get start tag for variables
    *
    * @access    public
    * @return    string    start tag
    */
    
function getStartTag()
    {
        return 
$this->_options['startTag'];
    }
    
   
/**
    * get end tag for variables
    *
    * @access    public
    * @return    string    end tag
    */
    
function getEndTag()
    {
        return 
$this->_options['endTag'];
    }
    
   
/**
    * add a directory where patTemplate should search for
    * modules.
    *
    * You may either pass a string or an array of directories.
    *
    * patTemplate will be searching for a module in the same
    * order you added them. If the module cannot be found in
    * the custom folders, it will look in
    * patTemplate/$moduleType.
    *
    * @access    public
    * @param    string            module type
    * @param    string|array    directory or directories to search.
    */
    
function addModuleDir$moduleType$dir )
    {
        if( !isset( 
$this->_moduleDirs[$moduleType] ) )
            
$this->_moduleDirs[$moduleType]    =    array();
        if( 
is_array$dir ) )
            
$this->_moduleDirs[$moduleType] = array_merge$this->_moduleDirs[$moduleType], $dir );
        else
            
array_push$this->_moduleDirs[$moduleType], $dir );
    }

   
/**
    * Sets an attribute of a template
    *
    * supported attributes: visibilty, loop, parse, unusedvars
    *
    * @param    string    $template    name of the template
    * @param    string    $attribute    name of the attribute
    * @param    mixed    $value    value of the attribute
    * @access    public
    * @see        setAttributes(),getAttribute(), clearAttribute()
    */
    
function setAttribute$template$attribute$value )
    {
        
$template    =    strtolower$template );
        if( !isset( 
$this->_templates[$template] ) )
        {
            return    
patErrorManager::raiseWarning(
                                                    
PATTEMPLATE_WARNING_NO_TEMPLATE,
                                                    
"Template '$template' does not exist."
                                                
);
        }

        
$attribute    =    strtolower$attribute );
        
$this->_templates[$template]['attributes'][$attribute]    =    $value;
        return 
true;
    }
    
   
/**
    * Sets several attribute of a template
    *
    * $attributes has to be a assotiative arrays containing attribute/value pairs
    * supported attributes: visibilty, loop, parse, unusedvars
    *
    * @param    string    $template    name of the template
    * @param    array    $attributes    attribute/value pairs
    * @access    public
    * @see        setAttribute(), getAttribute(), clearAttribute()
    */
    
function setAttributes$template$attributes )
    {
        if( !
is_array$attributes ) )
        {
            return 
patErrorManager::raiseErrorPATTEMPLATE_ERROR_EXPECTED_ARRAY'patTemplate::setAttributes: Expected array as second parameter, '.gettype$attributes ).' given' );
        }
        
        
$template    =    strtolower$template );
        
$attributes    =    array_change_key_case$attributes );
        if( !isset( 
$this->_templates[$template] ) )
        {
            return    
patErrorManager::raiseWarning(
                                                    
PATTEMPLATE_WARNING_NO_TEMPLATE,
                                                    
"Template '$template' does not exist."
                                                
);
        }

        
$this->_templates[$template]['attributes']    =    array_merge$this->_templates[$template]['attributes'], $attributes );
        return 
true;
    }

   
/**
    * Get all attributes of a template
    *
    * @param    string    name of the template
    * @return    array    attributes
    * @access    public
    */
    
function getAttributes$template )
    {
        
$template    =    strtolower$template );
        if( !isset( 
$this->_templates[$template] ) )
        {
            return    
patErrorManager::raiseWarning(
                                                    
PATTEMPLATE_WARNING_NO_TEMPLATE,
                                                    
"Template '$template' does not exist."
                                                
);
        }
        return    
$this->_templates[$template]['attributes'];
    }

   
/**
    * Gets an attribute of a template
    *
    * supported attributes: visibilty, loop, parse, unusedvars
    *
    * @param    string    $template    name of the template
    * @param    string    $attribute    name of the attribute
    * @return    mixed    value of the attribute
    * @access    public
    * @see        setAttribute(), setAttributes(), clearAttribute()
    */
    
function getAttribute$template$attribute )
    {
        
$template    =    strtolower$template );
        
$attribute    =    strtolower$attribute );
        if( !isset( 
$this->_templates[$template] ) )
        {
            return    
patErrorManager::raiseWarning(
                                                    
PATTEMPLATE_WARNING_NO_TEMPLATE,
                                                    
"Template '$template' does not exist."
                                                
);
        }
        return    
$this->_templates[$template]['attributes'][$attribute];
    }

   
/**
    * Clears an attribute of a template
    *
    * supported attributes: visibilty, loop, parse, unusedvars
    *
    * @param    string    $template    name of the template
    * @param    string    $attribute    name of the attribute
    * @access    public
    * @see        setAttribute(), setAttributes(), getAttribute()
    */
    
function clearAttribute$template$attribute )
    {
        
$template    =    strtolower$template );
        
$attribute    =    strtolower$attribute );

        if( !isset( 
$this->_templates[$template] ) )
        {
            return    
patErrorManager::raiseWarning(
                                                    
PATTEMPLATE_WARNING_NO_TEMPLATE,
                                                    
"Template '$template' does not exist."
                                                
);
        }
        
$this->_templates[$template]['attributes'][$attribute]    =    '';;
        return 
true;
    }

   
/**
    * Prepare a template
    *
    * This can be used if you want to add variables to
    * a template, that has not been loaded yet.
    *
    * @access    public
    * @param    string    template name
    */
    
function prepareTemplate$name )
    {
        
$name    =    strtolower$name );
        if( !isset( 
$this->_vars[$name] ) )
        {
            
$this->_vars[$name]    =    array(
                                                
'scalar'    =>    array(),
                                                
'rows'        =>    array()
                                            );
        }
    }
    
   
/**
    * add a variable to a template
    *
    * A variable may also be an indexed array, but _not_
    * an associative array!
    *
    * @access    public
    * @param    string    $template    name of the template
    * @param    string    $varname    name of the variable
    * @param    mixed    $value        value of the variable
    */
    
function addVar$template$varname$value )
    {
        
$template    =    strtolower$template );
        
$varname    =    strtoupper$varname );
        
        if( !
is_array$value ) )
        {
            
$this->_vars[$template]['scalar'][$varname]        =    $value;
            return 
true;
        }
        
        
$cnt    =    count$value );
        for( 
$i 0$i $cnt$i++ )
        {
            if( !isset( 
$this->_vars[$template]['rows'][$i] ) )
                
$this->_vars[$template]['rows'][$i]    =    array();

            
$this->_vars[$template]['rows'][$i][$varname]    =    $value[$i];
        }
        
        return 
true;
    }

   
/**
    * get the value of a variable
    *
    * @access    public
    * @param    string    name of the template
    * @param    string    name of the variable
    * @return    string    value of the variable, null if the variable is not set
    */
    
function getVar$template$varname )
    {
        
$template    =    strtolower$template );
        
$varname    =    strtoupper$varname );

        if( isset( 
$this->_vars[$template]['scalar'][$varname] ) )
            return 
$this->_vars[$template]['scalar'][$varname];

        
$value = array();

        
$cnt count$this->_vars[$template]['rows'] );
        for( 
$i 0$i $cnt$i++ )
        {
            if( !isset( 
$this->_vars[$template]['rows'][$i][$varname] ) )
                continue;
            
array_push$value$this->_vars[$template]['rows'][$i][$varname] );
        }
        if( !empty( 
$value ) )
            return 
$value;
        return 
null;
    }
    
   
/**
    * Adds several variables to a template
    *
    * Each Template can have an unlimited amount of its own variables
    * $variables has to be an assotiative array containing variable/value pairs
    *
    * @param    string    $template    name of the template
    * @param    array    $variables    assotiative array of the variables
    * @param    string    $prefix    prefix for all variable names
    * @access    public
    * @see        addVar(), addRows(), addGlobalVar(), addGlobalVars()
    */
    
function addVars$template$variables$prefix '' )
    {
        
$template    =    strtolower$template );
        
$prefix        =    strtoupper$prefix );
        
$variables    =    array_change_key_case$variablesCASE_UPPER );
        
        foreach( 
$variables as $varname => $value )
        {
            
$varname    =    $prefix.$varname;
            
            if( !
is_array$value ) ) {
                if (!
is_scalar($value)) {
                    continue;
                }
                
$this->_vars[$template]['scalar'][$varname]    =    $value;
                continue;
            }
            
            
$cnt    =    count$value );
            for( 
$i 0$i $cnt$i++ )
            {
                if( !isset( 
$this->_vars[$template]['rows'][$i] ) )
                    
$this->_vars[$template]['rows'][$i]    =    array();
    
                
$this->_vars[$template]['rows'][$i][$varname]    =    $value[$i];
            }
        }
    }

   
/**
    * Adds several rows of variables to a template
    *
    * Each Template can have an unlimited amount of its own variables
    * Can be used to add a database result as variables to a template
    *
    * @param    string    $template    name of the template
    * @param    array    $rows    array containing assotiative arrays with variable/value pairs
    * @param    string    $prefix    prefix for all variable names
    * @access    public
    * @see        addVar(), addVars(), addGlobalVar(), addGlobalVars()
    */
    
function addRows$template$rows$prefix '' )
    {
        
$template    =    strtolower$template );
        
$prefix        =    strtoupper$prefix );
            
        
$cnt        =    count$rows );
        for( 
$i 0$i $cnt$i++ )
        {
            if( !isset( 
$this->_vars[$template]['rows'][$i] ) )
                
$this->_vars[$template]['rows'][$i]    =    array();

            
$rows[$i]    =    array_change_key_case$rows[$i], CASE_UPPER );
                
            foreach( 
$rows[$i] as $varname => $value )
            {
                
$this->_vars[$template]['rows'][$i][$prefix.$varname]    =    $value;
            }
        }
    }

   
/**
    * Adds an object to a template
    *
    * All properties of the object will be available as template variables.
    *
    * @param    string            name of the template
    * @param    object|array    object or array of objects
    * @param    string            prefix for all variable names
    * @access    public
    * @see        addVar(), addRows(), addGlobalVar(), addGlobalVars()
    */
    
function addObject$template$object$prefix '' )
    {
        if( 
is_array$object ) )
        {
            
$rows = array();
            foreach( 
$object as $o )
                
array_push$rowsget_object_vars$o ) );

               
$this->addRows$template$rows$prefix );
            return 
true;
        }
        elseif( 
is_object$object ) )
        {
            
$this->addVars$templateget_object_vars$object ), $prefix );
            return 
true;
        }
        return 
false;
    }
    
   
/**
    * Adds a global variable
    *
    * Global variables are valid in all templates of this object.
    * A global variable has to be scalar, it will be converted to a string.
    *
    * @access    public
    * @param    string    $varname    name of the global variable
    * @param    string    $value        value of the variable
    * @return    boolean    true on success
    * @see        addGlobalVars(), addVar(), addVars(), addRows()
    */
    
function addGlobalVar$varname$value )
    {
        
$this->_globals[strtoupper$varname )]    =    ( string )$value;
        return    
true;
    }

   
/**
    * Adds several global variables
    *
    * Global variables are valid in all templates of this object.
    *
    * $variables is an associative array, containing name/value pairs of the variables.
    *
    * @access    public
    * @param    array    $variables    array containing the variables
    * @param    string    $prefix        prefix for variable names
    * @return    boolean    true on success
    * @see        addGlobalVar(), addVar(), addVars(), addRows()
    */
    
function addGlobalVars$variables$prefix '' )
    {
        
$variables    =    array_change_key_case$variablesCASE_UPPER );
        
$prefix        =    strtoupper$prefix );
        foreach( 
$variables as $varname => $value )
        {
            
$this->_globals[$prefix.$varname]    =    ( string )$value;
        }
        return    
true;
    }

   
/**
    * get all global variables
    *
    * @access    public
    * @return    array    global variables
    */
    
function getGlobalVars()
    {
        return    
$this->_globals;
    }
    
    
/**
    * checks wether a template exists
    *
    * @access    public
    * @param    string        name of the template
    * @return    boolean        true, if the template exists, false otherwise
    */
    
function exists$name )
    {
        return    
in_arraystrtolower$name ), $this->_templateList );
    }

   
/**
    * enable a template cache
    *
    * A template cache will improve performace, as the templates
    * do not have to be read on each request.
    *
    * @access    public
    * @param    string        name of the template cache
    * @param    array        parameters for the template cache
    * @return    boolean        true on success, patError otherwise
    */
    
function useTemplateCache$cache$params = array() )
    {
        if( !
is_object$cache ) )
        {
            
$cache = &$this->loadModule'TemplateCache'$cache$params );
        }
        if( 
patErrorManager::isError$cache ) )
            return 
$cache;

        
$this->_tmplCache = &$cache;
        return 
true;
    }

   
/**
    * enable an output filter
    *
    * Output filters are used to modify the template
    * result before it is sent to the browser.
    *
    * They are applied, when displayParsedTemplate() is called.
    *
    * @access    public
    * @param    string        name of the output filter
    * @param    array        parameters for the output filter
    * @return    boolean        true on success, patError otherwise
    */
    
function applyOutputFilter$filter$params = array() )
    {
        if( !
is_object$filter ) )
        {
            
$filter = &$this->loadModule'OutputFilter'$filter$params );
        }
        if( 
patErrorManager::isError$filter ) )
            return 
$filter;

        
$this->_outputFilters[] = &$filter;
        return 
true;
    }
    
   
/**
    * enable an input filter
    *
    * input filters are used to modify the template
    * stream before it is split into smaller templates-
    *
    * @access    public
    * @param    string        name of the input filter
    * @param    array        parameters for the input filter
    * @return    boolean        true on success, patError otherwise
    */
    
function applyInputFilter$filter$params = array() )
    {
        if( !
is_object$filter ) )
        {
            
$filter = &$this->loadModule'InputFilter'$filter$params );
        }
        if( 
patErrorManager::isError$filter ) )
            return 
$filter;

        
$this->_inputFilters[] = &$filter;
        return