PX : code

AvanTemplate by Naoki Shima
Download this code


<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Naoki Shima <naoki@avantexchange.com>                       |
// |                                                                      |
// +----------------------------------------------------------------------+
//
// $Id$

require_once PEAR.php;

/**
 * todo: 
 *       php generated template support - use ob_* function
 *       documentation
 *       variable declaration
 *       error code CONSTANTS
 *       error code to err text mapping
 *       ALT BLOCK support for multi-depth loop
 *       error handling
 *       argument checking
 *       example
 *       Utilize Cache from PEAR
 */

define(ALTALT);
define(END_END);
define(START_START);

class 
AvanTemplate extends PEAR
{
    
// {{{ properties

    /**
     * location or directory where template files reside
     * @var    string
     * @access private
     */
    
var $_location;

    
/**
     * contents of template file being parsed
     * @var    string
     * @access private
     */
    
var $_working;

    
/**
     * Which block to show, or hide
     * @var    array
     * @access private
     */
    
var $_block;

    
// }}}
    // {{{ constructor 

    /**
     * Initialize variable used by this class
     *
     * @param  string   (optional) root directory of the template file(s)
     *                  defaults to the same directory of the calling php script
     * @param  boolean  (optional) if string is needed to be handled multi-byte 
     *                  safe. defaults to false
     * @access public
     * @return void 
     */
    
function AvanTemplate($dir = , $multi_byte false)
    {
        
$this->_hide_unset true;
        
$this->_prepend = [%;
        
$this->_append = %];
        
$this->_cs = array(BLOCK_,LOOP_);
        
$this->_idx 1;
        
$this->_location $dir
        
$this->_multi_byte $multi_byte
        
$this->PEAR();
    }

    
// }}}
    // {{{ destructor

    /**
     * Does nothing right now
     *
     * @access public
     * @return void
     */
    
function _AvanTemplate()
    {
        
$this->_PEAR();
    }
  
    
// }}}
    // {{{ showUnset()

    /**
     * Show unset entity of template in finished content
     *
     * @access public
     * @return void
     */
    
function showUnset()
    {
        
$this->_hideUnset false;
    }

    
// }}}
    // {{{ hideBlock()

    /**
     * Hide named block from finished content
     * 
     * @param  string   Name of the block to hide
     * @access public
     * @return void
     */
    
function hideBlock($name)
    {
        
$this->_block[$name] = false;
    }

    
// }}}
    // {{{ showBlock()

    /**
     * Show named block from finished content
     *
     * @param  string   Name of the block to show
     * @access public
     * @return void
     */
    
function showBlock($name)
    {
        
$this->_block[$name] = true;
    }

    
// }}}
    // {{{ _generateUniqueId()

    /**
     * Generate random unique number
     * 
     * @access private
     * @return int
     */
    
function _generateUniqueId()
    {
        return 
$this->_idx++;
    }
   
    
// }}}
    // {{{ _getParentName()
   
    /**
     * Find name of the Parent entity
     *
     * @param  int       ID of entity
     * @access private
     * @return string 
     */
    
function _getParentName($id)
    {
        return 
$this->_parent_name[$id];
    }
  
    
// }}}
    // {{{ setChildValue()

    /**
     * Set value to the child of entity
     *
     * @param  int       Id of the parent entity
     * @param  string    Name of the entity
     * @param  mixed     Value to be set
     * @param  boolean   (optional) Whether to append the value to the existing 
     *                   value or override 
     * @access public
     * @return boolean   TRUE for success and FALSE for fail
     */
    
function setChildValue($parent_id$name$value$append false)
    {
        if(!
$this->_validateArray(&$value) && !$name) {
            return 
FALSE;
        }
        
$id $this->_generateUniqueId();
        
$parent_name $this->_getParentName($parent_id);
        if(
$this->_argument[$parent_name][$parent_id][$name] && $append) {
            
$this->_argument[$parent_name][$parent_id][$name][$id] = $value;
        } else {
            
$this->_argument[$parent_name][$parent_id][$name] = 
                array(
$id => $value);
        }
        return 
$id;
    }

    
// }}}
    // {{{ setValue()

    /**
     * Set value for variable interporation
     *
     * @param : string   name of the variable to replace
     * @param : mixed    value to replace the variable 
     * @param : boolean  (optional) whether to replace or append the value
     * 
     * @return: integer  ID of the entry. False is returned when params 
     *                   are invalid
     * @access: public
     */
    
function setValue($name$value$append false)
    {
        
$id TRUE;
        
$this->_validateArray(&$value);
        if(
$name && $value) {
            if(
is_array($value)) {
                
$id $this->_generateUniqueId();
                if(
$this->_argument[$name] && $append) {
                    
$this->_argument[$name][$id] = $value;
                } else {
                    
$this->_argument[$name] = array($id => $value);
                }
                
$this->_parent_name[$id] = $name;
            } else {
                
$this->_argument[$name] = $value;
            }
            return 
$id;
        } else {
            return 
FALSE;
        }
    }
    
    
// }}}
    // {{{ setList()

    /**
     * under development
     *
     *
     * @return: void
     * @access: public
     */ 
    
function setList($name,$list)
    {
        if(
$name && is_array($list)) {
            
$this->_argument[$name] = $list;
        }
    }

    
// }}}
    // {{{ setList()

    /*
     * under development
     */ 
    
function setValues($list)
    {
        if(
is_array($list)) {
            foreach(
$list AS $key => $value) {
                
$this->setValue($key,$value);
            }
        }
    }

    
// }}}

    
function setAppend($new
    {
        
$old $this->_append;
        
$this->_append $new;
        return 
$old;
    }

    function 
setPrepend($new
    {
        
$old $this->_prepend;
        
$this->_prepend $new;
        return 
$old;
    }

    function 
format($file$vals = )
    {
        
$is_vals $this->_validateArray(&$vals);
        if(
$this->_argument) {
            if(
$is_vals) {
                
// merge them if both exists
                
$this->_argument array_merge_recursive($vals,$this->_argument);
            } 
        } elseif(!
$is_vals) {
            
// none set, so raise error
            //return $this->raiseError($errstr, $errno);
            
return;
        } else {
            
// only $vals exists, then assign it to $this->_argument
            
$this->_argument $vals;
        }
        
$this->_loadTemplate($file);
        
$this->_loadExternalTemplate();
        
$this->_apply();
        
$this->_updateContents();
        return 
$this->_getContents();
    }
    
    
/*
     * Cast parameter $val to array if it is an object, and then checks if
     * $val is an array. If it is not, returns FALSE. 
     * Otherwise, this returns TRUE;
     */
    
function _validateArray(&$val)
    {
        if(
is_object($val)) {
            
$val = (array) $val;
        }
        if(
is_array($val)) {
            return 
true;
        } else {
            return 
FALSE;
        }
    }

    
/**
     *
     */
    
function _split($string$prepend$append$offset 0)
    {
        
$length $this->_strlen($prepend);
        
$append_length $this->_strlen($append);
        if(
$pos $this->_strpos($string,$prepend,$offset)){
            
$result[head] = $this->_substr($string$start$pos);
            
$end $pos+$length;
            
$pos $this->_strpos($string$append$pos);
            
$result[inside] = $this->_substr($string$end ,($pos-$end));
            
$end $pos+$append_length;
            
$result[tail] = $this->_substr($string$end);
            
$result[pos] = $end;
            return 
$result;
        }
        return 
FALSE;
    }

    
/**
     * Look for include statement in template file and substitute
     * include statement with the contents of template file specified.
     *
     * @return void
     * @access private
     */
    
function _loadExternalTemplate()
    {
        
$string $this->_working;
        
// load file whose name specified in template
        
$prepend $this->_prepend.INCLUDE_;
        while(
$result $this->_split($string,$prepend,$this->_append,$offset)){
            
$offset $result[pos];
            
$tmp $this->_readFile($this->_location.$result[inside]);
            
$string $result[head].$tmp.$result[tail];
        }
        
// load file whose name specified by includeFile()
        
if($this->_includeFiles) {
            foreach(
$this->_includeFiles AS $handle => $name) {
                
$needle $this->_prepend.INCLUDE:.$handle.$this->_append;
                
$length $this->_strlen($needle);
                while(
$pos $this->_strpos($string,$needle)){
                    
$tmp $this->_readFile($this->_location.$name);
                    
$string $this->_substr($string,0,$pos).$tmp.$this->_substr($string,($pos+$length));
                }
            }
        }
        
$this->_setWorking($string);
    }

    
/**
     * Update contents(result) with processed template.
     * Final processing is done in this function.
     *
     * @return void
     * @access private
     */
    
function _updateContents()
    {
        if(
$this->_hide_unset){
            
$this->_hideUnsetEntity();
        }
        
$this->_setContents($this->_working);
    }

    function 
_getAltBlock($string)
    {
        
$alt $this->_prepend.ALT.$this->_append;
        
$alt_length $this->_strlen($alt);
        if(
$alt_pos $this->_strpos($string,$alt)) {
            
$alt_start $alt_pos $alt_length;
            return 
$this->_substr($string,$alt_start); 
        }
        return 
FALSE;
    }

    function 
_hideUnsetEntity()
    {
        
$string $this->_working;
        
$append START.$this->_append;
        foreach(
$this->_cs AS $val) {
            
$prepend $this->_prepend.$val;
            while(
$tmp $this->_split($string,$prepend,$append)){
                
$name $tmp[inside]; //name of block
                
$needle $prepend.$name.END.$this->_append;
                
$pos $this->_strpos($string,$needle,$tmp[pos]);
                
$offset $pos $this->_strlen($needle);
                
$str $this->_substr($string,$tmp[pos],($pos-$tmp[pos]));
                
$alt_str $this->_getAltBlock($str);
                
$string $tmp[head].$alt_str.$this->_substr($string$offset);
            }
        } 
// end foreach
        
$offset 0;
        while(
$pos $this->_strpos($string$this->_prepend$offset)) {
            
$head $this->_substr($string0$pos);
            
$pos $this->_strpos($string$this->_append$pos);
            
$length $this->_strlen($this->_append);
            
$tail substr($string, ($pos+$length));
            
$offset $pos;
            
$string $head.$tail;
        }
        
$this->_setWorking($string);
    }

    function 
_apply()
    {
        if(
is_array($this->_block)) {
            foreach(
$this->_block AS $key => $value) {
                
$this->_formatBlock($key,$value);
            }
        }
        foreach(
$this->_argument AS $key => $value) {
            if(
is_array($value)) {
                
$this->_setWorking($this->_formatLoop($key,$value));
            } else {
                
$needle $this->_prepend.$key.$this->_append;
                
$this->_setWorking($this->_strReplace($needle$value));
            }
        }
    }
    
    
/**
     * Process BLOCK
     *
     * @param : string   Name of the Block
     * @param : boolean  Whether to show or hide the BLOCK
     *
     * @return: void
     * @access: private
     */
    
function _formatBlock($name$is_shown)
    {
        while(
$arr $this->_getControlStructure($name,BLOCK)){
            if(
$is_shown) {
                
$arr[inside] = $this->_stripAltBlock($arr[inside]);
            } else {
                
$arr[inside] = $this->_getAltBlock($arr[inside]);
            }
            
$this->_setWorking($arr[head].$arr[inside].$arr[tail]);
        }
    }

    
/**
     * Process LOOP
     *
     * @param : string   Name of the loop
     * @param : mixed    Value to replace(interporate) the variable
     * @param : string   (optional) string in which it looks for LOOP
     *
     * @return: string   Processed string
     * @access: private
     */
    
function _formatLoop($item$vals$string=)
    {
        if(!
$string) {
            
$string $this->_working;
        }
        while(
$arr $this->_getControlStructure($item,LOOP,$string)){
            unset(
$result);
            foreach(
$vals AS $val) {
                if(
$this->_validateArray(&$val)) {
                    unset(
$tmp);
                    foreach(
$val AS $key => $value) {
                        if(!
$tmp) {
                            
$tmp $arr[inside];
                        }
                        if(
is_array($value)) {
                            
$tmp $this->_formatLoop($item...$key,$value,$tmp);
                            continue 
1;
                        } 
                        
$needle $this->_prepend.$item...$key.$this->_append;
                        
$tmp $this->_stripAltBlock($tmp);
                        
$tmp $this->_strReplace($needle,$value,$tmp);
                    }
                    
$result .= $tmp;
                }
            }
            
$string $arr[head].$result.$arr[tail];
        }
        return 
$string;
    }

    
/**
     * Strip ALT BLOCK from passed string
     * @access: private
     */
    
function _stripAltBlock($string)
    {
        
$alt $this->_prepend.ALT.$this->_append;
        
$pos $this->_strrpos($stringEND.$this->_append);
        if((
$alt_pos $this->_strrpos($string,$alt)) && (!$pos || ($pos && ($pos $alt_pos)))) {
            return 
$this->_substr($string,0,$alt_pos);
        }
        return 
$string;
    }

    function 
_setWorking($val)
    {
        
$old $this->_working;
        
$this->_working $val;
        return 
$old;
    }

    function 
_getControlStructure($item,$type,$string = )
    {
        if(!
$string) {
            
$string $this->_working;
        }
        
$needle $this->_prepend.$type._.$item.START.$this->_append;
        
$length $this->_strlen($needle);
        if(
$pos $this->_strpos($string,$needle)){
            
$result[head] = $this->_substr($string0$pos);
            
$end $pos+$length;
            
$needle $this->_prepend.$type._.$item.END.$this->_append;
            
$pos $this->_strpos($string$needle$pos);
            
$result[inside] = $this->_substr($string$end ,($pos-$end));
            
$end $pos+$this->_strlen($needle);
            
$result[tail] = $this->_substr($string$end);
            return 
$result;
        } else {
            return 
FALSE;
        }
    }

    
/**
     * Get contents stored in the object
     *
     * @access private
     * @return string
     */
    
function _getContents()
    {
        if(!
$this->_contents) {
            return;
            
// return $this->raiseError($errstr, $errno);
        
}
        return 
$this->_contents;
    } 

    
/**
     * Wrapper function for str_replace(). Multi-byte safe.
     * Exactly the same API as str_replace()
     * Refer manual for str_replace() 
     * @param  mixed 
     * @param  mixed  
     * @param  mixed
     *
     * @access private
     * @return mixed
     */
    
function _strReplace($search$replace$subject = )
    {
        if(!
$subject) { 
            
$subject $this->_working;
        }
        if(!
$this->_multi_byte) {
            return 
str_replace($search$replace$subject);
        } 
        if(
is_array($subject)) {
            foreach(
$subject AS $sub) {
                if(
is_array($search)) {
                    for(
$i=0count($search) >$i$i++) {
                        
$s $search[$i];
                        if(
is_array($replace)) {
                            
$r $replace[$i]; 
                        } else {
                            
$r $replace;
                        }
                        
$result[] = $this->_strReplaceLtd($s,$r,$sub);
                    }
                } else {
                    
$result[] = $this->_strReplaceLtd($search,$replace,$sub);
                }
            }
        } else {
            
$result $this->_strReplaceLtd($search,$replace,$subject);
        }
        return 
$result;
    }

    
/*
     * only used by _strReplace
     * @access private
     */
    
function _strReplaceLtd($search$replace$subject)
    {
        
$offset 0;
        if(!(
$length = @mb_strlen($search))){
            return;
            
// return $this->raiseError($errstr, $errno);
        
}
        while(
$pos = @mb_strpos($subject,$search,$offset)) {
            
$end $pos+$length;
            
$subject mb_substr($subject0$pos).$replace
                       
.$this->_substr($subject,$end);
            
$offset $end;
        }
        return 
$subject;
    }

    
//wrapper function -- for multi_byte
    
function _strpos($string,$needle,$offset 0)
    {
        if(
$this->_multi_byte) {
            return @
mb_strpos($string$needle$offset);
        }
        return @
strpos($string$needle$offset);
    }

    
/**
     * Wraps strrpos function so that it calls mb_strrpos when multi-byte is 
     * specified and handle string as its needle. PHP native function only
     * handle single character as needle.
     *
     * @param : string  Heystack
     * @param : string  Needle
     * 
     * @return: int     
     * @access: private
     */
    
function _strrpos($heystack$needle)
    {
        if(
$this->_multi_byte) {
            return @
mb_strrpos($heystack$needle);
        }
        
$tmp 0;
        while(
$pos strpos($heystack$needle$tmp)) {
            
$tmp $pos 1;
        }
        return (
$tmp 1);
    }

    
//wrapper function -- for multi_byte
    
function _strlen($value)
    {
        if(
$this->_multi_byte) {
            return @
mb_strlen($value);
        }
        return 
strlen($value);
    }

    
//wrapper function -- for multi_byte
    
function _substr($string$start$length = )
    {
        if(
$this->_multi_byte) {
            if(
$length) {
                return 
mb_substr($string,$start,$length);
            }
            return 
mb_substr($string,$start);
        } else {
            if(
$length) {
                return 
substr($string,$start,$length);
            }
            return 
substr($string,$start);
        }
    }
  
    function 
_loadTemplate($file
    {
        
$this->_template $this->_readFile($this->_location.$file);
        
$this->_setWorking($this->_template);
    }
  
    function 
_readFile($file)
    {
        
$fp fopen($filer-);
        if(!
is_resource($fp)) {
            return;
        }
        while(
$data fread($fp2048)) {
            
$contents .= $data;
        }
        
fclose($fp);
        return 
$contents;
    }

    function 
_setContents($val)
    {
        
$old $this->_contents;
        
$this->_contents $val;
        return 
$old;
    }
   
    function 
includeFile($handle$filename)
    {
        
$this->_includeFiles[$handle] = $filename;
    }
}

?>

Comments or questions?
PX is running PHP 5.2.17
Thanks to Miranda Productions for hosting and bandwidth.
Use of any code from PX is at your own risk.