267 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			267 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Context object
							 | 
						||
| 
								 | 
							
								 *  encapsulate context, resolve name
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class H2o_Context implements ArrayAccess {
							 | 
						||
| 
								 | 
							
								    public $safeClass = array('stdClass', 'BlockContext');
							 | 
						||
| 
								 | 
							
								    public $scopes;
							 | 
						||
| 
								 | 
							
								    public $options;
							 | 
						||
| 
								 | 
							
								    public $autoescape = true;
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    private $arrayMethods = array('first'=> 0, 'last'=> 1, 'length'=> 2, 'size'=> 3);
							 | 
						||
| 
								 | 
							
								    static $lookupTable = array();
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function __construct($context = array(), $options = array()){
							 | 
						||
| 
								 | 
							
								        if (is_object($context))
							 | 
						||
| 
								 | 
							
								           $context = get_object_vars($context);
							 | 
						||
| 
								 | 
							
								        $this->scopes = array($context);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if (isset($options['safeClass'])) 
							 | 
						||
| 
								 | 
							
								            $this->safeClass = array_merge($this->safeClass, $options['safeClass']);
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								        if (isset($options['autoescape'])) 
							 | 
						||
| 
								 | 
							
								            $this->autoescape = $options['autoescape'];
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								        $this->options = $options;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function push($layer = array()){
							 | 
						||
| 
								 | 
							
								        return array_unshift($this->scopes, $layer);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * pop the most recent layer
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    function pop() {
							 | 
						||
| 
								 | 
							
								        if (!isset($this->scopes[1]))
							 | 
						||
| 
								 | 
							
								            throw new Exception('cannnot pop from empty stack');
							 | 
						||
| 
								 | 
							
								        return array_shift($this->scopes);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function offsetExists($offset) {
							 | 
						||
| 
								 | 
							
								        foreach ($this->scopes as $layer) {
							 | 
						||
| 
								 | 
							
								            if (isset($layer[$offset])) return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function offsetGet($key) {
							 | 
						||
| 
								 | 
							
								        foreach ($this->scopes as $layer) {
							 | 
						||
| 
								 | 
							
								            if (isset($layer[$key]))
							 | 
						||
| 
								 | 
							
								                return $layer[$key];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function offsetSet($key, $value) {
							 | 
						||
| 
								 | 
							
								        if (strpos($key, '.') > -1)
							 | 
						||
| 
								 | 
							
								            throw new Exception('cannot set non local variable');
							 | 
						||
| 
								 | 
							
								        return $this->scopes[0][$key] = $value;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function offsetUnset($key) {
							 | 
						||
| 
								 | 
							
								        foreach ($this->scopes as $layer) {
							 | 
						||
| 
								 | 
							
								            if (isset($layer[$key])) unset($layer[$key]);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function extend($context) {
							 | 
						||
| 
								 | 
							
								        $this->scopes[0] = array_merge($this->scopes[0], $context);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function set($key, $value) {
							 | 
						||
| 
								 | 
							
								        return $this->offsetSet($key, $value);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function get($key) {
							 | 
						||
| 
								 | 
							
								        return $this->offsetGet($key);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function isDefined($key) {
							 | 
						||
| 
								 | 
							
								        return $this->offsetExists($key);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     *  Variable name
							 | 
						||
| 
								 | 
							
								     * 
							 | 
						||
| 
								 | 
							
								     * @param $var variable name or array(0 => variable name, 'filters' => filters array)
							 | 
						||
| 
								 | 
							
								     * @return unknown_type
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    function resolve($var) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # if $var is array - it contains filters to apply
							 | 
						||
| 
								 | 
							
								        $filters = array();
							 | 
						||
| 
								 | 
							
								        if ( is_array($var) ) {
							 | 
						||
| 
								 | 
							
								        	
							 | 
						||
| 
								 | 
							
								            $name = array_shift($var);
							 | 
						||
| 
								 | 
							
								            $filters = isset($var['filters'])? $var['filters'] : array();
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        } 
							 | 
						||
| 
								 | 
							
								        else $name = $var;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        $result = null;
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
								        # Lookup basic types, null, boolean, numeric and string
							 | 
						||
| 
								 | 
							
								        # Variable starts with : (:users.name) to short-circuit lookup
							 | 
						||
| 
								 | 
							
								        if ($name[0] === ':') {
							 | 
						||
| 
								 | 
							
								            $object =  $this->getVariable(substr($name, 1));
							 | 
						||
| 
								 | 
							
								            if (!is_null($object)) $result = $object;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            if ($name === 'true') {
							 | 
						||
| 
								 | 
							
								                $result = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            elseif ($name === 'false') {
							 | 
						||
| 
								 | 
							
								                $result = false;
							 | 
						||
| 
								 | 
							
								            } 
							 | 
						||
| 
								 | 
							
								            elseif (preg_match('/^-?\d+(\.\d+)?$/', $name, $matches)) {
							 | 
						||
| 
								 | 
							
								                $result = isset($matches[1])? floatval($name) : intval($name);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            elseif (preg_match('/^"([^"\\\\]*(?:\\.[^"\\\\]*)*)"|' .
							 | 
						||
| 
								 | 
							
								                           '\'([^\'\\\\]*(?:\\.[^\'\\\\]*)*)\'$/', $name)) {            
							 | 
						||
| 
								 | 
							
								                $result = stripcslashes(substr($name, 1, -1));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!empty(self::$lookupTable) && $result == Null) {
							 | 
						||
| 
								 | 
							
								            $result = $this->externalLookup($name);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        $result = $this->applyFilters($result,$filters);
							 | 
						||
| 
								 | 
							
								        return $result;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								    function getVariable($name) {
							 | 
						||
| 
								 | 
							
								        # Local variables. this gives as a bit of performance improvement
							 | 
						||
| 
								 | 
							
								        if (!strpos($name, '.'))
							 | 
						||
| 
								 | 
							
								            return $this[$name];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Prepare for Big lookup
							 | 
						||
| 
								 | 
							
								        $parts = explode('.', $name);
							 | 
						||
| 
								 | 
							
								        $object = $this[array_shift($parts)];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Lookup context
							 | 
						||
| 
								 | 
							
								        foreach ($parts as $part) {
							 | 
						||
| 
								 | 
							
								            if (is_array($object) or $object instanceof ArrayAccess) {
							 | 
						||
| 
								 | 
							
								                if (isset($object[$part])) {
							 | 
						||
| 
								 | 
							
								                    $object = $object[$part];
							 | 
						||
| 
								 | 
							
								                } elseif ($part === 'first') {
							 | 
						||
| 
								 | 
							
								                    $object = isset($object[0])?$object[0]:null;
							 | 
						||
| 
								 | 
							
								                } elseif ($part === 'last') {
							 | 
						||
| 
								 | 
							
								                    $last = count($object)-1;
							 | 
						||
| 
								 | 
							
								                    $object = isset($object[$last])?$object[$last]:null;
							 | 
						||
| 
								 | 
							
								                } elseif ($part === 'size' or $part === 'length') {
							 | 
						||
| 
								 | 
							
								                    return count($object);
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    return null;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            elseif (is_object($object)) {
							 | 
						||
| 
								 | 
							
								                if (isset($object->$part))
							 | 
						||
| 
								 | 
							
								                    $object = $object->$part;
							 | 
						||
| 
								 | 
							
								                elseif (is_callable(array($object, $part))) {
							 | 
						||
| 
								 | 
							
								                    $methodAllowed = in_array(get_class($object), $this->safeClass) || 
							 | 
						||
| 
								 | 
							
								                        (isset($object->h2o_safe) && (
							 | 
						||
| 
								 | 
							
								                            $object->h2o_safe === true || in_array($part, $object->h2o_safe)
							 | 
						||
| 
								 | 
							
								                        )
							 | 
						||
| 
								 | 
							
								                    );
							 | 
						||
| 
								 | 
							
								                    $object = $methodAllowed ? $object->$part() : null;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else return null;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else return null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return $object;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function applyFilters($object, $filters) {
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        foreach ($filters as $filter) {
							 | 
						||
| 
								 | 
							
								            $name = substr(array_shift($filter), 1);
							 | 
						||
| 
								 | 
							
								            $args = $filter;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (isset(h2o::$filters[$name])) {                
							 | 
						||
| 
								 | 
							
								                foreach ($args as $i => $argument) {
							 | 
						||
| 
								 | 
							
								                    # name args
							 | 
						||
| 
								 | 
							
								                    if (is_array($argument)) {
							 | 
						||
| 
								 | 
							
								                        foreach ($argument as $n => $arg) {
							 | 
						||
| 
								 | 
							
								                            $args[$i][$n] = $this->resolve($arg);
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                    # resolve argument values
							 | 
						||
| 
								 | 
							
								                       $args[$i] = $this->resolve($argument);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                array_unshift($args, $object);
							 | 
						||
| 
								 | 
							
								                $object = call_user_func_array(h2o::$filters[$name], $args);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return $object;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function escape($value, $var) {
							 | 
						||
| 
								 | 
							
										
							 | 
						||
| 
								 | 
							
								        $safe = false;
							 | 
						||
| 
								 | 
							
								        $filters = (is_array($var) && isset($var['filters']))? $var['filters'] : array();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        foreach ( $filters as $filter ) {
							 | 
						||
| 
								 | 
							
								        	
							 | 
						||
| 
								 | 
							
								            $name = substr(array_shift($filter), 1);
							 | 
						||
| 
								 | 
							
								            $safe = !$safe && ($name === 'safe');
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								            $escaped = $name === 'escape';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        $should_escape = $this->autoescape || isset($escaped) && $escaped;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if ( ($should_escape && !$safe)) {
							 | 
						||
| 
								 | 
							
								            $value = htmlspecialchars($value);
							 | 
						||
| 
								 | 
							
								        }		
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return $value;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function externalLookup($name) {
							 | 
						||
| 
								 | 
							
								        if (!empty(self::$lookupTable)) {
							 | 
						||
| 
								 | 
							
								            foreach (self::$lookupTable as $lookup) {
							 | 
						||
| 
								 | 
							
								                $tmp = call_user_func_array($lookup, array($name, $this));
							 | 
						||
| 
								 | 
							
								                if ($tmp !== null)
							 | 
						||
| 
								 | 
							
								                return $tmp;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class BlockContext {
							 | 
						||
| 
								 | 
							
								    var $h2o_safe = array('name', 'depth', 'super');
							 | 
						||
| 
								 | 
							
								    var $block, $index;
							 | 
						||
| 
								 | 
							
								    private $context;
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function __construct($block, $context, $index) {
							 | 
						||
| 
								 | 
							
								        $this->block =& $block;
							 | 
						||
| 
								 | 
							
								        $this->context = $context;
							 | 
						||
| 
								 | 
							
								        $this->index = $index;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function name() {
							 | 
						||
| 
								 | 
							
								        return $this->block->name;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function depth() {
							 | 
						||
| 
								 | 
							
								        return $this->index;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function super() {
							 | 
						||
| 
								 | 
							
								        $stream = new StreamWriter;
							 | 
						||
| 
								 | 
							
								        $this->block->parent->render($this->context, $stream, $this->index+1);
							 | 
						||
| 
								 | 
							
								        return $stream->close(); 
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function __toString() {
							 | 
						||
| 
								 | 
							
								        return "[BlockContext : {$this->block->name}, {$this->block->filename}]";
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								?>
							 |