// /** * This class handles template execution context. * @package phptal */ class PHPTAL_Context { public static $USE_GLOBAL = false; public $__line = false; public $__file = false; public $__repeat; public $__xmlDeclaration; public $__docType; public $__nothrow; public $__translator; public function __construct() { $this->__repeat = new StdClass(); } public function __clone() { $this->__repeat = clone($this->__repeat); } public function setParent(PHPTAL_Context $parent) { $this->_parentContext = $parent; } public function setGlobal(StdClass $globalContext) { $this->_globalContext = $globalContext; } public function pushContext() { if (self::$USE_GLOBAL) return $this; $res = clone $this; $res->setParent($this); return $res; } public function popContext() { if (self::$USE_GLOBAL) return $this; return $this->_parentContext; } /** * Set output document type if not already set. * * This method ensure PHPTAL uses the first DOCTYPE encountered (main * template or any macro template source containing a DOCTYPE. */ public function setDocType($doctype) { if ($this->_parentContext != null){ return $this->_parentContext->setDocType($doctype); } if ($this->_parentContext != null){ return $this->_parentContext->setDocType($doctype); } if (!$this->__docType){ $this->__docType = $doctype; } } /** * Set output document xml declaration. * * This method ensure PHPTAL uses the first xml declaration encountered * (main template or any macro template source containing an xml * declaration). */ public function setXmlDeclaration($xmldec) { if ($this->_parentContext != null){ return $this->_parentContext->setXmlDeclaration($xmldec); } if ($this->_parentContext != null){ return $this->_parentContext->setXmlDeclaration($xmldec); } if (!$this->__xmlDeclaration){ $this->__xmlDeclaration = $xmldec; } } /** * Activate or deactivate exception throwing during unknown path * resolution. */ public function noThrow($bool) { $this->__nothrow = $bool; } /** * Returns true if specified slot is filled. */ public function hasSlot($key) { if ($this->_parentContext) return $this->_parentContext->hasSlot($key); // setting slots in any context return array_key_exists($key, $this->_slots); } /** * Returns the content of specified filled slot. */ public function getSlot($key) { if ($this->_parentContext) return $this->_parentContext->getSlot($key); // setting slots in any context return $this->_slots[$key]; } /** * Fill a macro slot. */ public function fillSlot($key, $content) { if ($this->_parentContext) $this->_parentContext->fillSlot($key,$content); // setting slots in any context else $this->_slots[$key] = $content; } /** * Push current filled slots on stack. */ public function pushSlots() { array_push($this->_slotsStack, $this->_slots); $this->_slots = array(); } /** * Restore filled slots stack. */ public function popSlots() { $this->_slots = array_pop($this->_slotsStack); } /** * Context setter. */ public function __set($varname, $value) { if ($varname[0] == '_') { throw new PHPTAL_Exception('Template variable error \''.$varname.'\' must not begin with underscore'); } $this->$varname = $value; } /** * Context getter. */ public function __get($varname) { if ($varname == 'repeat') return $this->__repeat; if (isset($this->$varname)){ return $this->$varname; } if (isset($this->_globalContext->$varname)){ return $this->_globalContext->$varname; } if (defined($varname)) { return constant($varname); } if ($this->__nothrow) return null; $e = sprintf('Unable to find path %s in current scope', $varname); throw new PHPTAL_Exception($e, $this->__file, $this->__line); } private $_slots = array(); private $_slotsStack = array(); private $_parentContext = null; private $_globalContext = null; } // emulate property_exists() function, this is slow but much better than // isset(), use next release of PHP5 as soon as available ! if (!function_exists('property_exists')){ function property_exists($o, $property) { return array_key_exists($property, get_object_vars($o)); } } /** * Resolve TALES path starting from the first path element. * * The TALES path : object/method1/10/method2 * will call : phptal_path($ctx->object, 'method1/10/method2') * * The nothrow param is used by phptal_exists() and prevent this function to * throw an exception when a part of the path cannot be resolved, null is * returned instead. */ function phptal_path($base, $path, $nothrow=false) {//{{{ $parts = explode('/', $path); $current = true; if ($base === null) { if ($nothrow) return null; throw new PHPTAL_Exception("Trying to read property '$path' from NULL"); } while (($current = array_shift($parts)) !== null){ // object handling if (is_object($base)){ // look for method if (method_exists($base, $current)){ $base = $base->$current(); continue; } // look for variable if (property_exists($base, $current)){ $base = $base->$current; continue; } if ($base instanceof ArrayAccess && $base->offsetExists($current)) { $base = $base->offsetGet($current); continue; } if ($base instanceof Countable && ($current === 'length' || $current === 'size')) { $base = count($base); continue; } // look for isset (priority over __get) if (method_exists($base,'__isset') && is_callable(array($base, '__isset'))) { if ($base->__isset($current)){ $base = $base->$current; continue; } } // ask __get and discard if it returns null else if (method_exists($base,'__get') && is_callable(array($base, '__get'))) { $tmp = $base->$current; if (NULL !== $tmp){ $base = $tmp; continue; } } // magic method call if (method_exists($base, '__call')){ try { $base = $base->$current(); continue; } catch(BadMethodCallException $e){} } // emulate array behaviour if (is_numeric($current) && method_exists($base, '__getAt')){ $base = $base->__getAt($current); continue; } if ($nothrow) return null; $err = 'Unable to find part "%s" in path "%s" inside '.(is_object($base)?get_class($base):gettype($base)); $err = sprintf($err, $current, $path); throw new PHPTAL_Exception($err); } // array handling if (is_array($base)) { // key or index if (array_key_exists((string)$current, $base)){ $base = $base[$current]; continue; } // virtual methods provided by phptal if ($current == 'length' || $current == 'size'){ $base = count($base); continue; } if ($nothrow) return null; $err = 'Unable to find array key "%s" in path "%s"'; $err = sprintf($err, $current, $path); throw new PHPTAL_Exception($err); } // string handling if (is_string($base)) { // virtual methods provided by phptal if ($current == 'length' || $current == 'size'){ $base = strlen($base); continue; } // access char at index if (is_numeric($current)){ $base = $base[$current]; continue; } } // if this point is reached, then the part cannot be resolved if ($nothrow) return null; $err = 'Unable to find part "%s" in path "%s" with base "%s"'; $err = sprintf($err, $current, $path, is_scalar($base)?"$base":(is_object($base)?get_class($base):gettype($base))); throw new PHPTAL_Exception($err); } return $base; } function phptal_true($ctx, $path) { $ctx->noThrow(true); $res = phptal_path($ctx, $path, true); $ctx->noThrow(false); return !!$res; } /** * Returns true if $path can be fully resolved in $ctx context. */ function phptal_exists($ctx, $path) { // special note: this method may requires to be extended to a full // phptal_path() sibling to avoid calling latest path part if it is a // method or a function... $ctx->noThrow(true); $res = phptal_path($ctx, $path, true); $ctx->noThrow(false); return $res !== NULL; } function phptal_isempty($var) { return $var === null || $var === false || $var === '' || ((is_array($var) || $var instanceof Countable) && count($var)===0); } function phptal_escape($var, $ent, $encoding) { if (is_string($var)) { return htmlspecialchars($var, $ent, $encoding); } elseif (is_object($var)) { if ($var instanceof SimpleXMLElement) return $var->asXML(); return htmlspecialchars($var->__toString(), $ent, $encoding); } elseif (is_bool($var)){ return (int)$var; } return $var; } ?>