Login   Register  
PHP Classes
elePHPant
Icontem

File: AT.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Naoki Shima  >  avantemplate  >  AT.php  >  Download  
File: AT.php
Role: ???
Content type: text/plain
Description: AvanTemplate Ver. 1.2.2 stable
Class: avantemplate
Author: By
Last change:
Date: 2001-11-07 09:03
Size: 21,441 bytes
 

Contents

Class file image Download
<?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: 
 *       parse template once, store and use parsed template
 *       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('ALT', 'ALT');
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($string, 0, $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($string, END.$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($string, 0, $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=0; count($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($subject, 0, $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($file, 'r-');
        if(!is_resource($fp)) {
            return;
        }
        while($data = fread($fp, 2048)) {
            $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;
    }
}

?>