home *** CD-ROM | disk | FTP | other *** search
- <?php
- /**
- * Implementation of Integrated Templates API with template 'compilation' added.
- *
- * PHP versions 4 and 5
- *
- * LICENSE: This source file is subject to version 3.01 of the PHP license
- * that is available through the world-wide-web at the following URI:
- * http://www.php.net/license/3_01.txt If you did not receive a copy of
- * the PHP License and are unable to obtain it through the web, please
- * send a note to license@php.net so we can mail you a copy immediately.
- *
- * @category HTML
- * @package HTML_Template_Sigma
- * @author Ulf Wendel <ulf.wendel@phpdoc.de>
- * @author Alexey Borzov <avb@php.net>
- * @copyright 2001-2007 The PHP Group
- * @license http://www.php.net/license/3_01.txt PHP License 3.01
- * @version CVS: $Id: Sigma.php,v 1.17 2007/05/19 13:31:19 avb Exp $
- * @link http://pear.php.net/package/HTML_Template_Sigma
- */
-
- /**
- * PEAR and PEAR_Error classes (for error handling)
- */
- require_once 'PEAR.php';
-
- /**#@+
- * Error codes
- * @see HTML_Template_Sigma::errorMessage()
- */
- define('SIGMA_OK', 1);
- define('SIGMA_ERROR', -1);
- define('SIGMA_TPL_NOT_FOUND', -2);
- define('SIGMA_BLOCK_NOT_FOUND', -3);
- define('SIGMA_BLOCK_DUPLICATE', -4);
- define('SIGMA_CACHE_ERROR', -5);
- define('SIGMA_UNKNOWN_OPTION', -6);
- define('SIGMA_PLACEHOLDER_NOT_FOUND', -10);
- define('SIGMA_PLACEHOLDER_DUPLICATE', -11);
- define('SIGMA_BLOCK_EXISTS', -12);
- define('SIGMA_INVALID_CALLBACK', -13);
- define('SIGMA_CALLBACK_SYNTAX_ERROR', -14);
- /**#@-*/
-
- /**
- * Implementation of Integrated Templates API with template 'compilation' added.
- *
- * The main new feature in Sigma is the template 'compilation'. Consider the
- * following: when loading a template file the engine has to parse it using
- * regular expressions to find all the blocks and variable placeholders. This
- * is a very "expensive" operation and is definitely an overkill to do on
- * every page request: templates seldom change on production websites. This is
- * where the cache kicks in: it saves an internal representation of the
- * template structure into a file and this file gets loaded instead of the
- * source one on subsequent requests (unless the source changes, of course).
- *
- * While HTML_Template_Sigma inherits PHPLib Template's template syntax, it has
- * an API which is easier to understand. When using HTML_Template_PHPLIB, you
- * have to explicitly name a source and a target the block gets parsed into.
- * This gives maximum flexibility but requires full knowledge of template
- * structure from the programmer.
- *
- * Integrated Template on the other hands manages block nesting and parsing
- * itself. The engine knows that inner1 is a child of block2, there's
- * no need to tell it about this:
- *
- * <pre>
- * + __global__ (hidden and automatically added)
- * + block1
- * + block2
- * + inner1
- * + inner2
- * </pre>
- *
- * To add content to block1 you simply type:
- * <code>$tpl->setCurrentBlock("block1");</code>
- * and repeat this as often as needed:
- * <code>
- * $tpl->setVariable(...);
- * $tpl->parseCurrentBlock();
- * </code>
- *
- * To add content to block2 you would type something like:
- * <code>
- * $tpl->setCurrentBlock("inner1");
- * $tpl->setVariable(...);
- * $tpl->parseCurrentBlock();
- *
- * $tpl->setVariable(...);
- * $tpl->parseCurrentBlock();
- *
- * $tpl->parse("block2");
- * </code>
- *
- * This will result in one repetition of block2 which contains two repetitions
- * of inner1. inner2 will be removed if $removeEmptyBlock is set to true (which
- * is the default).
- *
- * Usage:
- * <code>
- * $tpl = new HTML_Template_Sigma( [string filerootdir], [string cacherootdir] );
- *
- * // load a template or set it with setTemplate()
- * $tpl->loadTemplatefile( string filename [, boolean removeUnknownVariables, boolean removeEmptyBlocks] )
- *
- * // set "global" Variables meaning variables not beeing within a (inner) block
- * $tpl->setVariable( string variablename, mixed value );
- *
- * // like with the HTML_Template_PHPLIB there's a second way to use setVariable()
- * $tpl->setVariable( array ( string varname => mixed value ) );
- *
- * // Let's use any block, even a deeply nested one
- * $tpl->setCurrentBlock( string blockname );
- *
- * // repeat this as often as you need it.
- * $tpl->setVariable( array ( string varname => mixed value ) );
- * $tpl->parseCurrentBlock();
- *
- * // get the parsed template or print it: $tpl->show()
- * $html = $tpl->get();
- * </code>
- *
- * @category HTML
- * @package HTML_Template_Sigma
- * @author Ulf Wendel <ulf.wendel@phpdoc.de>
- * @author Alexey Borzov <avb@php.net>
- * @version Release: 1.1.6
- */
- class HTML_Template_Sigma extends PEAR
- {
- /**
- * First character of a variable placeholder ( _{_VARIABLE} ).
- * @var string
- * @access public
- * @see $closingDelimiter, $blocknameRegExp, $variablenameRegExp
- */
- var $openingDelimiter = '{';
-
- /**
- * Last character of a variable placeholder ( {VARIABLE_}_ )
- * @var string
- * @access public
- * @see $openingDelimiter, $blocknameRegExp, $variablenameRegExp
- */
- var $closingDelimiter = '}';
-
- /**
- * RegExp for matching the block names in the template.
- * Per default "sm" is used as the regexp modifier, "i" is missing.
- * That means a case sensitive search is done.
- * @var string
- * @access public
- * @see $variablenameRegExp, $openingDelimiter, $closingDelimiter
- */
- var $blocknameRegExp = '[0-9A-Za-z_-]+';
-
- /**
- * RegExp matching a variable placeholder in the template.
- * Per default "sm" is used as the regexp modifier, "i" is missing.
- * That means a case sensitive search is done.
- * @var string
- * @access public
- * @see $blocknameRegExp, $openingDelimiter, $closingDelimiter
- */
- var $variablenameRegExp = '[0-9A-Za-z._-]+';
-
- /**
- * RegExp used to find variable placeholder, filled by the constructor
- * @var string Looks somewhat like @(delimiter varname delimiter)@
- * @see HTML_Template_Sigma()
- */
- var $variablesRegExp = '';
-
- /**
- * RegExp used to strip unused variable placeholders
- * @see $variablesRegExp, HTML_Template_Sigma()
- */
- var $removeVariablesRegExp = '';
-
- /**
- * RegExp used to find blocks and their content, filled by the constructor
- * @var string
- * @see HTML_Template_Sigma()
- */
- var $blockRegExp = '';
-
- /**
- * Controls the handling of unknown variables, default is remove
- * @var boolean
- * @access public
- */
- var $removeUnknownVariables = true;
-
- /**
- * Controls the handling of empty blocks, default is remove
- * @var boolean
- * @access public
- */
- var $removeEmptyBlocks = true;
-
- /**
- * Name of the current block
- * @var string
- */
- var $currentBlock = '__global__';
-
- /**
- * Template blocks and their content
- * @var array
- * @see _buildBlocks()
- * @access private
- */
- var $_blocks = array();
-
- /**
- * Content of parsed blocks
- * @var array
- * @see get(), parse()
- * @access private
- */
- var $_parsedBlocks = array();
-
- /**
- * Variable names that appear in the block
- * @var array
- * @see _buildBlockVariables()
- * @access private
- */
- var $_blockVariables = array();
-
- /**
- * Inner blocks inside the block
- * @var array
- * @see _buildBlocks()
- * @access private
- */
- var $_children = array();
-
- /**
- * List of blocks to preserve even if they are "empty"
- * @var array
- * @see touchBlock(), $removeEmptyBlocks
- * @access private
- */
- var $_touchedBlocks = array();
-
- /**
- * List of blocks which should not be shown even if not "empty"
- * @var array
- * @see hideBlock(), $removeEmptyBlocks
- * @access private
- */
- var $_hiddenBlocks = array();
-
- /**
- * Variables for substitution.
- *
- * Variables are kept in this array before the replacements are done.
- * This allows automatic removal of empty blocks.
- *
- * @var array
- * @see setVariable()
- * @access private
- */
- var $_variables = array();
-
- /**
- * Global variables for substitution
- *
- * These are substituted into all blocks, are not cleared on
- * block parsing and do not trigger "non-empty" logic. I.e. if
- * only global variables are substituted into the block, it is
- * still considered "empty".
- *
- * @var array
- * @see setVariable(), setGlobalVariable()
- * @access private
- */
- var $_globalVariables = array();
-
- /**
- * Root directory for "source" templates
- * @var string
- * @see HTML_Template_Sigma(), setRoot()
- */
- var $fileRoot = '';
-
- /**
- * Directory to store the "prepared" templates in
- * @var string
- * @see HTML_Template_Sigma(), setCacheRoot()
- * @access private
- */
- var $_cacheRoot = null;
-
- /**
- * Flag indicating that the global block was parsed
- * @var boolean
- */
- var $flagGlobalParsed = false;
-
- /**
- * Options to control some finer aspects of Sigma's work.
- *
- * @var array
- * @access private
- */
- var $_options = array(
- 'preserve_data' => false,
- 'trim_on_save' => true
- );
-
- /**
- * Function name prefix used when searching for function calls in the template
- * @var string
- */
- var $functionPrefix = 'func_';
-
- /**
- * Function name RegExp
- * @var string
- */
- var $functionnameRegExp = '[_a-zA-Z]+[A-Za-z_0-9]*';
-
- /**
- * RegExp used to grep function calls in the template (set by the constructor)
- * @var string
- * @see _buildFunctionlist(), HTML_Template_Sigma()
- */
- var $functionRegExp = '';
-
- /**
- * List of functions found in the template.
- * @var array
- * @access private
- */
- var $_functions = array();
-
- /**
- * List of callback functions specified by the user
- * @var array
- * @access private
- */
- var $_callback = array();
-
- /**
- * RegExp used to find file inclusion calls in the template (should have 'e' modifier)
- * @var string
- */
- var $includeRegExp = '#<!--\s+INCLUDE\s+(\S+)\s+-->#ime';
-
- /**
- * Files queued for inclusion
- * @var array
- * @access private
- */
- var $_triggers = array();
-
-
- /**
- * Constructor: builds some complex regular expressions and optionally
- * sets the root directories.
- *
- * Make sure that you call this constructor if you derive your template
- * class from this one.
- *
- * @param string root directory for templates
- * @param string directory to cache "prepared" templates in
- * @see setRoot(), setCacheRoot()
- */
- function HTML_Template_Sigma($root = '', $cacheRoot = '')
- {
- // the class is inherited from PEAR to be able to use $this->setErrorHandling()
- $this->PEAR();
- $this->variablesRegExp = '@' . $this->openingDelimiter . '(' . $this->variablenameRegExp . ')' .
- '(:(' . $this->functionnameRegExp . '))?' . $this->closingDelimiter . '@sm';
- $this->removeVariablesRegExp = '@'.$this->openingDelimiter.'\s*('.$this->variablenameRegExp.')\s*'.$this->closingDelimiter.'@sm';
- $this->blockRegExp = '@<!--\s+BEGIN\s+('.$this->blocknameRegExp.')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
- $this->functionRegExp = '@' . $this->functionPrefix . '(' . $this->functionnameRegExp . ')\s*\(@sm';
- $this->setRoot($root);
- $this->setCacheRoot($cacheRoot);
-
- $this->setCallbackFunction('h', 'htmlspecialchars');
- $this->setCallbackFunction('u', 'urlencode');
- $this->setCallbackFunction('j', array(&$this, '_jsEscape'));
- }
-
-
- /**
- * Sets the file root for templates. The file root gets prefixed to all
- * filenames passed to the object.
- *
- * @param string directory name
- * @see HTML_Template_Sigma()
- * @access public
- */
- function setRoot($root)
- {
- if (('' != $root) && (DIRECTORY_SEPARATOR != substr($root, -1))) {
- $root .= DIRECTORY_SEPARATOR;
- }
- $this->fileRoot = $root;
- }
-
-
- /**
- * Sets the directory to cache "prepared" templates in, the directory should be writable for PHP.
- *
- * The "prepared" template contains an internal representation of template
- * structure: essentially a serialized array of $_blocks, $_blockVariables,
- * $_children and $_functions, may also contain $_triggers. This allows
- * to bypass expensive calls to _buildBlockVariables() and especially
- * _buildBlocks() when reading the "prepared" template instead of
- * the "source" one.
- *
- * The files in this cache do not have any TTL and are regenerated when the
- * source templates change.
- *
- * @param string directory name
- * @see HTML_Template_Sigma(), _getCached(), _writeCache()
- * @access public
- */
- function setCacheRoot($root)
- {
- if (empty($root)) {
- $root = null;
- } elseif (DIRECTORY_SEPARATOR != substr($root, -1)) {
- $root .= DIRECTORY_SEPARATOR;
- }
- $this->_cacheRoot = $root;
- }
-
-
- /**
- * Sets the option for the template class
- *
- * Currently available options:
- * - preserve_data: If false (default), then substitute variables and
- * remove empty placeholders in data passed through setVariable (see also
- * PHP bugs #20199, #21951)
- * - trim_on_save: Whether to trim extra whitespace from template on cache
- * save (defaults to true). Generally safe to leave this on, unless you
- * have <<pre>><</pre>> in templates or want to preserve HTML indentantion
- *
- * @access public
- * @param string option name
- * @param mixed option value
- * @return mixed SIGMA_OK on success, error object on failure
- */
- function setOption($option, $value)
- {
- if (isset($this->_options[$option])) {
- $this->_options[$option] = $value;
- return SIGMA_OK;
- }
- return $this->raiseError($this->errorMessage(SIGMA_UNKNOWN_OPTION, $option), SIGMA_UNKNOWN_OPTION);
- }
-
-
- /**
- * Returns a textual error message for an error code
- *
- * @access public
- * @param integer error code
- * @param string additional data to insert into message
- * @return string error message
- */
- function errorMessage($code, $data = null)
- {
- static $errorMessages;
- if (!isset($errorMessages)) {
- $errorMessages = array(
- SIGMA_ERROR => 'unknown error',
- SIGMA_OK => '',
- SIGMA_TPL_NOT_FOUND => 'Cannot read the template file \'%s\'',
- SIGMA_BLOCK_NOT_FOUND => 'Cannot find block \'%s\'',
- SIGMA_BLOCK_DUPLICATE => 'The name of a block must be unique within a template. Block \'%s\' found twice.',
- SIGMA_CACHE_ERROR => 'Cannot save template file \'%s\'',
- SIGMA_UNKNOWN_OPTION => 'Unknown option \'%s\'',
- SIGMA_PLACEHOLDER_NOT_FOUND => 'Variable placeholder \'%s\' not found',
- SIGMA_PLACEHOLDER_DUPLICATE => 'Placeholder \'%s\' should be unique, found in multiple blocks',
- SIGMA_BLOCK_EXISTS => 'Block \'%s\' already exists',
- SIGMA_INVALID_CALLBACK => 'Callback does not exist',
- SIGMA_CALLBACK_SYNTAX_ERROR => 'Cannot parse template function: %s'
- );
- }
-
- if (PEAR::isError($code)) {
- $code = $code->getCode();
- }
- if (!isset($errorMessages[$code])) {
- return $errorMessages[SIGMA_ERROR];
- } else {
- return (null === $data)? $errorMessages[$code]: sprintf($errorMessages[$code], $data);
- }
- }
-
-
- /**
- * Prints a block with all replacements done.
- *
- * @access public
- * @param string block name
- * @see get()
- */
- function show($block = '__global__')
- {
- print $this->get($block);
- }
-
-
- /**
- * Returns a block with all replacements done.
- *
- * @param string block name
- * @param bool whether to clear parsed block contents
- * @return string block with all replacements done
- * @throws PEAR_Error
- * @access public
- * @see show()
- */
- function get($block = '__global__', $clear = false)
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- if ('__global__' == $block && !$this->flagGlobalParsed) {
- $this->parse('__global__');
- }
- // return the parsed block, removing the unknown placeholders if needed
- if (!isset($this->_parsedBlocks[$block])) {
- return '';
-
- } else {
- $ret = $this->_parsedBlocks[$block];
- if ($clear) {
- unset($this->_parsedBlocks[$block]);
- }
- if ($this->removeUnknownVariables) {
- $ret = preg_replace($this->removeVariablesRegExp, '', $ret);
- }
- if ($this->_options['preserve_data']) {
- $ret = str_replace($this->openingDelimiter . '%preserved%' . $this->closingDelimiter, $this->openingDelimiter, $ret);
- }
- return $ret;
- }
- }
-
-
- /**
- * Parses the given block.
- *
- * @param string block name
- * @param boolean true if the function is called recursively (do not set this to true yourself!)
- * @param boolean true if parsing a "hidden" block (do not set this to true yourself!)
- * @access public
- * @see parseCurrentBlock()
- * @throws PEAR_Error
- */
- function parse($block = '__global__', $flagRecursion = false, $fakeParse = false)
- {
- static $vars;
-
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- if ('__global__' == $block) {
- $this->flagGlobalParsed = true;
- }
- if (!isset($this->_parsedBlocks[$block])) {
- $this->_parsedBlocks[$block] = '';
- }
- $outer = $this->_blocks[$block];
-
- if (!$flagRecursion) {
- $vars = array();
- }
- // block is not empty if its local var is substituted
- $empty = true;
- foreach ($this->_blockVariables[$block] as $allowedvar => $v) {
- if (isset($this->_variables[$allowedvar])) {
- $vars[$this->openingDelimiter . $allowedvar . $this->closingDelimiter] = $this->_variables[$allowedvar];
- $empty = false;
- // vital for checking "empty/nonempty" status
- unset($this->_variables[$allowedvar]);
- }
- }
-
- // processing of the inner blocks
- if (isset($this->_children[$block])) {
- foreach ($this->_children[$block] as $innerblock => $v) {
- $placeholder = $this->openingDelimiter.'__'.$innerblock.'__'.$this->closingDelimiter;
-
- if (isset($this->_hiddenBlocks[$innerblock])) {
- // don't bother actually parsing this inner block; but we _have_
- // to go through its local vars to prevent problems on next iteration
- $this->parse($innerblock, true, true);
- unset($this->_hiddenBlocks[$innerblock]);
- $outer = str_replace($placeholder, '', $outer);
-
- } else {
- $this->parse($innerblock, true, $fakeParse);
- // block is not empty if its inner block is not empty
- if ('' != $this->_parsedBlocks[$innerblock]) {
- $empty = false;
- }
-
- $outer = str_replace($placeholder, $this->_parsedBlocks[$innerblock], $outer);
- $this->_parsedBlocks[$innerblock] = '';
- }
- }
- }
-
- // add "global" variables to the static array
- foreach ($this->_globalVariables as $allowedvar => $value) {
- if (isset($this->_blockVariables[$block][$allowedvar])) {
- $vars[$this->openingDelimiter . $allowedvar . $this->closingDelimiter] = $value;
- }
- }
- // if we are inside a hidden block, don't bother
- if (!$fakeParse) {
- if (0 != count($vars) && (!$flagRecursion || !empty($this->_functions[$block]))) {
- $varKeys = array_keys($vars);
- $varValues = $this->_options['preserve_data']? array_map(array(&$this, '_preserveOpeningDelimiter'), array_values($vars)): array_values($vars);
- }
-
- // check whether the block is considered "empty" and append parsed content if not
- if (!$empty || ('__global__' == $block) || !$this->removeEmptyBlocks || isset($this->_touchedBlocks[$block])) {
- // perform callbacks
- if (!empty($this->_functions[$block])) {
- foreach ($this->_functions[$block] as $id => $data) {
- $placeholder = $this->openingDelimiter . '__function_' . $id . '__' . $this->closingDelimiter;
- // do not waste time calling function more than once
- if (!isset($vars[$placeholder])) {
- $args = array();
- $preserveArgs = isset($this->_callback[$data['name']]['preserveArgs']) && $this->_callback[$data['name']]['preserveArgs'];
- foreach ($data['args'] as $arg) {
- $args[] = (empty($varKeys) || $preserveArgs)? $arg: str_replace($varKeys, $varValues, $arg);
- }
- if (isset($this->_callback[$data['name']]['data'])) {
- $res = call_user_func_array($this->_callback[$data['name']]['data'], $args);
- } else {
- $res = isset($args[0])? $args[0]: '';
- }
- $outer = str_replace($placeholder, $res, $outer);
- // save the result to variable cache, it can be requested somewhere else
- $vars[$placeholder] = $res;
- }
- }
- }
- // substitute variables only on non-recursive call, thus all
- // variables from all inner blocks get substituted
- if (!$flagRecursion && !empty($varKeys)) {
- $outer = str_replace($varKeys, $varValues, $outer);
- }
-
- $this->_parsedBlocks[$block] .= $outer;
- if (isset($this->_touchedBlocks[$block])) {
- unset($this->_touchedBlocks[$block]);
- }
- }
- }
- return $empty;
- }
-
-
- /**
- * Sets a variable value.
- *
- * The function can be used either like setVariable("varname", "value")
- * or with one array $variables["varname"] = "value" given setVariable($variables)
- *
- * @access public
- * @param mixed variable name or array ('varname'=>'value')
- * @param string variable value if $variable is not an array
- */
- function setVariable($variable, $value = '')
- {
- if (is_array($variable)) {
- $this->_variables = array_merge($this->_variables, $variable);
- } elseif (!is_array($variable) && is_array($value)) {
- $this->_variables = array_merge($this->_variables, $this->_flattenVariables($variable, $value));
- } else {
- $this->_variables[$variable] = $value;
- }
- }
-
-
- /**
- * Sets a global variable value.
- *
- * @access public
- * @param mixed variable name or array ('varname'=>'value')
- * @param string variable value if $variable is not an array
- * @see setVariable()
- */
- function setGlobalVariable($variable, $value = '')
- {
- if (is_array($variable)) {
- $this->_globalVariables = array_merge($this->_globalVariables, $variable);
- } else if (!is_array($variable) && is_array($value)) {
- $this->_globalVariables = array_merge($this->_globalVariables, $this->_flattenVariables($variable, $value));
- } else {
- $this->_globalVariables[$variable] = $value;
- }
- }
-
-
- /**
- * Sets the name of the current block: the block where variables are added
- *
- * @param string block name
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @access public
- */
- function setCurrentBlock($block = '__global__')
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- $this->currentBlock = $block;
- return SIGMA_OK;
- }
-
-
- /**
- * Parses the current block
- *
- * @see parse(), setCurrentBlock()
- * @access public
- */
- function parseCurrentBlock()
- {
- return $this->parse($this->currentBlock);
- }
-
-
- /**
- * Returns the current block name
- *
- * @return string block name
- * @access public
- */
- function getCurrentBlock()
- {
- return $this->currentBlock;
- }
-
-
- /**
- * Preserves the block even if empty blocks should be removed.
- *
- * Sometimes you have blocks that should be preserved although they are
- * empty (no placeholder replaced). Think of a shopping basket. If it's
- * empty you have to show a message to the user. If it's filled you have
- * to show the contents of the shopping basket. Now where to place the
- * message that the basket is empty? It's not a good idea to place it
- * in you application as customers tend to like unecessary minor text
- * changes. Having another template file for an empty basket means that
- * one fine day the filled and empty basket templates will have different
- * layouts.
- *
- * So blocks that do not contain any placeholders but only messages like
- * "Your shopping basked is empty" are intoduced. Now if there is no
- * replacement done in such a block the block will be recognized as "empty"
- * and by default ($removeEmptyBlocks = true) be stripped off. To avoid this
- * you can call touchBlock()
- *
- * @param string block name
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @access public
- * @see $removeEmptyBlocks, $_touchedBlocks
- */
- function touchBlock($block)
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- if (isset($this->_hiddenBlocks[$block])) {
- unset($this->_hiddenBlocks[$block]);
- }
- $this->_touchedBlocks[$block] = true;
- return SIGMA_OK;
- }
-
-
- /**
- * Hides the block even if it is not "empty".
- *
- * Is somewhat an opposite to touchBlock().
- *
- * Consider a block (a 'edit' link for example) that should be visible to
- * registered/"special" users only, but its visibility is triggered by
- * some little 'id' field passed in a large array into setVariable(). You
- * can either carefully juggle your variables to prevent the block from
- * appearing (a fragile solution) or simply call hideBlock()
- *
- * @param string block name
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @access public
- */
- function hideBlock($block)
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- if (isset($this->_touchedBlocks[$block])) {
- unset($this->_touchedBlocks[$block]);
- }
- $this->_hiddenBlocks[$block] = true;
- return SIGMA_OK;
- }
-
-
- /**
- * Sets the template.
- *
- * You can either load a template file from disk with LoadTemplatefile() or set the
- * template manually using this function.
- *
- * @access public
- * @param string template content
- * @param boolean remove unknown/unused variables?
- * @param boolean remove empty blocks?
- * @return mixed SIGMA_OK on success, error object on failure
- * @see loadTemplatefile()
- */
- function setTemplate($template, $removeUnknownVariables = true, $removeEmptyBlocks = true)
- {
- $this->_resetTemplate($removeUnknownVariables, $removeEmptyBlocks);
- $list = $this->_buildBlocks('<!-- BEGIN __global__ -->'.$template.'<!-- END __global__ -->');
- if (PEAR::isError($list)) {
- return $list;
- }
- return $this->_buildBlockVariables();
- }
-
-
- /**
- * Loads a template file.
- *
- * If caching is on, then it checks whether a "prepared" template exists.
- * If it does, it gets loaded instead of the original, if it does not, then
- * the original gets loaded and prepared and then the prepared version is saved.
- * addBlockfile() and replaceBlockfile() implement quite the same logic.
- *
- * @param string filename
- * @param boolean remove unknown/unused variables?
- * @param boolean remove empty blocks?
- * @access public
- * @return mixed SIGMA_OK on success, error object on failure
- * @see setTemplate(), $removeUnknownVariables, $removeEmptyBlocks
- */
- function loadTemplateFile($filename, $removeUnknownVariables = true, $removeEmptyBlocks = true)
- {
- if ($this->_isCached($filename)) {
- $this->_resetTemplate($removeUnknownVariables, $removeEmptyBlocks);
- return $this->_getCached($filename);
- }
- $template = $this->_getFile($this->fileRoot . $filename);
- if (PEAR::isError($template)) {
- return $template;
- }
- $this->_triggers = array();
- $template = preg_replace($this->includeRegExp, "\$this->_makeTrigger('\\1', '__global__')", $template);
- if (SIGMA_OK !== ($res = $this->setTemplate($template, $removeUnknownVariables, $removeEmptyBlocks))) {
- return $res;
- } else {
- return $this->_writeCache($filename, '__global__');
- }
- }
-
-
- /**
- * Adds a block to the template changing a variable placeholder to a block placeholder.
- *
- * This means that a new block will be integrated into the template in
- * place of a variable placeholder. The variable placeholder will be
- * removed and the new block will behave in the same way as if it was
- * inside the original template.
- *
- * The block content must not start with <!-- BEGIN blockname --> and end with
- * <!-- END blockname -->, if it does the error will be thrown.
- *
- * @param string name of the variable placeholder, the name must be unique within the template.
- * @param string name of the block to be added
- * @param string content of the block
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @see addBlockfile()
- * @access public
- */
- function addBlock($placeholder, $block, $template)
- {
- if (isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_EXISTS, $block), SIGMA_BLOCK_EXISTS);
- }
- $parents = $this->_findParentBlocks($placeholder);
- if (0 == count($parents)) {
- return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_NOT_FOUND, $placeholder), SIGMA_PLACEHOLDER_NOT_FOUND);
- } elseif (count($parents) > 1) {
- return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_DUPLICATE, $placeholder), SIGMA_PLACEHOLDER_DUPLICATE);
- }
-
- $template = "<!-- BEGIN $block -->" . $template . "<!-- END $block -->";
- $list = $this->_buildBlocks($template);
- if (PEAR::isError($list)) {
- return $list;
- }
- $this->_replacePlaceholder($parents[0], $placeholder, $block);
- return $this->_buildBlockVariables($block);
- }
-
-
- /**
- * Adds a block taken from a file to the template, changing a variable placeholder
- * to a block placeholder.
- *
- * @param string name of the variable placeholder
- * @param string name of the block to be added
- * @param string template file that contains the block
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @see addBlock()
- * @access public
- */
- function addBlockfile($placeholder, $block, $filename)
- {
- if ($this->_isCached($filename)) {
- return $this->_getCached($filename, $block, $placeholder);
- }
- $template = $this->_getFile($this->fileRoot . $filename);
- if (PEAR::isError($template)) {
- return $template;
- }
- $template = preg_replace($this->includeRegExp, "\$this->_makeTrigger('\\1', '{$block}')", $template);
- if (SIGMA_OK !== ($res = $this->addBlock($placeholder, $block, $template))) {
- return $res;
- } else {
- return $this->_writeCache($filename, $block);
- }
- }
-
-
- /**
- * Replaces an existing block with new content.
- *
- * This function will replace a block of the template and all blocks
- * contained in it and add a new block instead. This means you can
- * dynamically change your template.
- *
- * Sigma analyses the way you've nested blocks and knows which block
- * belongs into another block. This nesting information helps to make the
- * API short and simple. Replacing blocks does not only mean that Sigma
- * has to update the nesting information (relatively time consuming task)
- * but you have to make sure that you do not get confused due to the
- * template change yourself.
- *
- * @param string name of a block to replace
- * @param string new content
- * @param boolean true if the parsed contents of the block should be kept
- * @access public
- * @see replaceBlockfile(), addBlock()
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- */
- function replaceBlock($block, $template, $keepContent = false)
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- // should not throw a error as we already checked for block existance
- $this->_removeBlockData($block, $keepContent);
- $template = "<!-- BEGIN $block -->" . $template . "<!-- END $block -->";
-
- $list = $this->_buildBlocks($template);
- if (PEAR::isError($list)) {
- return $list;
- }
- // renew the variables list
- return $this->_buildBlockVariables($block);
- }
-
-
- /**
- * Replaces an existing block with new content from a file.
- *
- * @access public
- * @param string name of a block to replace
- * @param string template file that contains the block
- * @param boolean true if the parsed contents of the block should be kept
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @see replaceBlock(), addBlockfile()
- */
- function replaceBlockfile($block, $filename, $keepContent = false)
- {
- if ($this->_isCached($filename)) {
- if (PEAR::isError($res = $this->_removeBlockData($block, $keepContent))) {
- return $res;
- } else {
- return $this->_getCached($filename, $block);
- }
- }
- $template = $this->_getFile($this->fileRoot . $filename);
- if (PEAR::isError($template)) {
- return $template;
- }
- $template = preg_replace($this->includeRegExp, "\$this->_makeTrigger('\\1', '{$block}')", $template);
- if (SIGMA_OK !== ($res = $this->replaceBlock($block, $template, $keepContent))) {
- return $res;
- } else {
- return $this->_writeCache($filename, $block);
- }
- }
-
-
- /**
- * Checks if the block exists in the template
- *
- * @param string block name
- * @return bool
- * @access public
- */
- function blockExists($block)
- {
- return isset($this->_blocks[$block]);
- }
-
-
- /**
- * Returns the name of the (first) block that contains the specified placeholder.
- *
- * @param string Name of the placeholder you're searching
- * @param string Name of the block to scan. If left out (default) all blocks are scanned.
- * @return string Name of the (first) block that contains the specified placeholder.
- * If the placeholder was not found an empty string is returned.
- * @access public
- * @throws PEAR_Error
- */
- function placeholderExists($placeholder, $block = '')
- {
- if ('' != $block && !isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- if ('' != $block) {
- // if we search in the specific block, we should just check the array
- return isset($this->_blockVariables[$block][$placeholder])? $block: '';
- } else {
- // _findParentBlocks returns an array, we need only the first element
- $parents = $this->_findParentBlocks($placeholder);
- return empty($parents)? '': $parents[0];
- }
- } // end func placeholderExists
-
-
- /**
- * Sets a callback function.
- *
- * Sigma templates can contain simple function calls. This means that the
- * author of the template can add a special placeholder to it:
- * <pre>
- * func_h1("embedded in h1")
- * </pre>
- * Sigma will parse the template for these placeholders and will allow
- * you to define a callback function for them. Callback will be called
- * automatically when the block containing such function call is parse()'d.
- *
- * Please note that arguments to these template functions can contain
- * variable placeholders: func_translate('Hello, {username}'), but not
- * blocks or other function calls.
- *
- * This should NOT be used to add logic (except some presentation one) to
- * the template. If you use a lot of such callbacks and implement business
- * logic through them, then you're reinventing the wheel. Consider using
- * XML/XSLT, native PHP or some other template engine.
- *
- * <code>
- * function h_one($arg) {
- * return '<h1>' . $arg . '</h1>';
- * }
- * ...
- * $tpl = new HTML_Template_Sigma( ... );
- * ...
- * $tpl->setCallbackFunction('h1', 'h_one');
- * </code>
- *
- * template:
- * <pre>
- * func_h1('H1 Headline');
- * </pre>
- *
- * @param string Function name in the template
- * @param mixed A callback: anything that can be passed to call_user_func_array()
- * @param bool If true, then no variable substitution in arguments will take place before function call
- * @return mixed SIGMA_OK on success, error object on failure
- * @throws PEAR_Error
- * @access public
- */
- function setCallbackFunction($tplFunction, $callback, $preserveArgs = false)
- {
- if (!is_callable($callback)) {
- return $this->raiseError($this->errorMessage(SIGMA_INVALID_CALLBACK), SIGMA_INVALID_CALLBACK);
- }
- $this->_callback[$tplFunction] = array(
- 'data' => $callback,
- 'preserveArgs' => $preserveArgs
- );
- return SIGMA_OK;
- } // end func setCallbackFunction
-
-
- /**
- * Returns a list of blocks within a template.
- *
- * If $recursive is false, it returns just a 'flat' array of $parent's
- * direct subblocks. If $recursive is true, it builds a tree of template
- * blocks using $parent as root. Tree structure is compatible with
- * PEAR::Tree's Memory_Array driver.
- *
- * @param string parent block name
- * @param bool whether to return a tree of child blocks (true) or a 'flat' array (false)
- * @access public
- * @return array a list of child blocks
- * @throws PEAR_Error
- */
- function getBlockList($parent = '__global__', $recursive = false)
- {
- if (!isset($this->_blocks[$parent])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $parent), SIGMA_BLOCK_NOT_FOUND);
- }
- if (!$recursive) {
- return isset($this->_children[$parent])? array_keys($this->_children[$parent]): array();
- } else {
- $ret = array('name' => $parent);
- if (!empty($this->_children[$parent])) {
- $ret['children'] = array();
- foreach (array_keys($this->_children[$parent]) as $child) {
- $ret['children'][] = $this->getBlockList($child, true);
- }
- }
- return $ret;
- }
- }
-
-
- /**
- * Returns a list of placeholders within a block.
- *
- * Only 'normal' placeholders are returned, not auto-created ones.
- *
- * @param string block name
- * @access public
- * @return array a list of placeholders
- * @throws PEAR_Error
- */
- function getPlaceholderList($block = '__global__')
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- $ret = array();
- foreach ($this->_blockVariables[$block] as $var => $v) {
- if ('__' != substr($var, 0, 2) || '__' != substr($var, -2)) {
- $ret[] = $var;
- }
- }
- return $ret;
- }
-
-
- /**
- * Clears the variables
- *
- * Global variables are not affected. The method is useful when you add
- * a lot of variables via setVariable() and are not sure whether all of
- * them appear in the block you parse(). If you clear the variables after
- * parse(), you don't risk them suddenly showing up in other blocks.
- *
- * @access public
- * @see setVariable()
- */
- function clearVariables()
- {
- $this->_variables = array();
- }
-
-
- //------------------------------------------------------------
- //
- // Private methods follow
- //
- //------------------------------------------------------------
-
- /**
- * Builds the variable names for nested variables
- *
- * @param string variable name
- * @param array value array
- * @return array array with 'name.key' keys
- * @access private
- */
- function _flattenVariables($name, $array)
- {
- $ret = array();
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- $ret = array_merge($ret, $this->_flattenVariables($name . '.' . $key, $value));
- } else {
- $ret[$name . '.' . $key] = $value;
- }
- }
- return $ret;
- }
-
- /**
- * Reads the file and returns its content
- *
- * @param string filename
- * @return string file content (or error object)
- * @access private
- */
- function _getFile($filename)
- {
- if (!($fh = @fopen($filename, 'rb'))) {
- return $this->raiseError($this->errorMessage(SIGMA_TPL_NOT_FOUND, $filename), SIGMA_TPL_NOT_FOUND);
- }
- $content = fread($fh, max(1, filesize($filename)));
- fclose($fh);
- return $content;
- }
-
-
- /**
- * Recursively builds a list of all variables within a block.
- *
- * Also calls _buildFunctionlist() for each block it visits
- *
- * @param string block name
- * @see _buildFunctionlist()
- * @access private
- */
- function _buildBlockVariables($block = '__global__')
- {
- $this->_blockVariables[$block] = array();
- $this->_functions[$block] = array();
- preg_match_all($this->variablesRegExp, $this->_blocks[$block], $regs, PREG_SET_ORDER);
- foreach ($regs as $match) {
- $this->_blockVariables[$block][$match[1]] = true;
- if (!empty($match[3])) {
- $funcData = array(
- 'name' => $match[3],
- 'args' => array($this->openingDelimiter . $match[1] . $this->closingDelimiter)
- );
- $funcId = substr(md5(serialize($funcData)), 0, 10);
-
- // update block info
- $this->_blocks[$block] = str_replace($match[0], $this->openingDelimiter . '__function_' . $funcId . '__' . $this->closingDelimiter, $this->_blocks[$block]);
- $this->_blockVariables[$block]['__function_' . $funcId . '__'] = true;
- $this->_functions[$block][$funcId] = $funcData;
- }
- }
- if (SIGMA_OK != ($res = $this->_buildFunctionlist($block))) {
- return $res;
- }
- if (isset($this->_children[$block]) && is_array($this->_children[$block])) {
- foreach ($this->_children[$block] as $child => $v) {
- if (SIGMA_OK != ($res = $this->_buildBlockVariables($child))) {
- return $res;
- }
- }
- }
- return SIGMA_OK;
- }
-
-
- /**
- * Recusively builds a list of all blocks within the template.
- *
- * @param string template to be scanned
- * @see $_blocks
- * @throws PEAR_Error
- * @return mixed array of block names on success or error object on failure
- * @access private
- */
- function _buildBlocks($string)
- {
- $blocks = array();
- if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
- foreach ($regs as $k => $match) {
- $blockname = $match[1];
- $blockcontent = $match[2];
- if (isset($this->_blocks[$blockname]) || isset($blocks[$blockname])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_DUPLICATE, $blockname), SIGMA_BLOCK_DUPLICATE);
- }
- $this->_blocks[$blockname] = $blockcontent;
- $blocks[$blockname] = true;
- $inner = $this->_buildBlocks($blockcontent);
- if (PEAR::isError($inner)) {
- return $inner;
- }
- foreach ($inner as $name => $v) {
- $pattern = sprintf('@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm', $name, $name);
- $replacement = $this->openingDelimiter.'__'.$name.'__'.$this->closingDelimiter;
- $this->_blocks[$blockname] = preg_replace($pattern, $replacement, $this->_blocks[$blockname]);
- $this->_children[$blockname][$name] = true;
- }
- }
- }
- return $blocks;
- }
-
-
- /**
- * Resets the object's properties, used before processing a new template
- *
- * @param boolean remove unknown/unused variables?
- * @param boolean remove empty blocks?
- * @see setTemplate(), loadTemplateFile()
- * @access private
- */
- function _resetTemplate($removeUnknownVariables = true, $removeEmptyBlocks = true)
- {
- $this->removeUnknownVariables = $removeUnknownVariables;
- $this->removeEmptyBlocks = $removeEmptyBlocks;
- $this->currentBlock = '__global__';
- $this->_variables = array();
- $this->_blocks = array();
- $this->_children = array();
- $this->_parsedBlocks = array();
- $this->_touchedBlocks = array();
- $this->_functions = array();
- $this->flagGlobalParsed = false;
- } // _resetTemplate
-
-
- /**
- * Checks whether we have a "prepared" template cached.
- *
- * If we do not do caching, always returns false
- *
- * @access private
- * @param string source filename
- * @return bool yes/no
- * @see loadTemplatefile(), addBlockfile(), replaceBlockfile()
- */
- function _isCached($filename)
- {
- if (null === $this->_cacheRoot) {
- return false;
- }
- $cachedName = $this->_cachedName($filename);
- $sourceName = $this->fileRoot . $filename;
- // if $sourceName does not exist, error will be thrown later
- $sourceTime = @filemtime($sourceName);
- if ((false !== $sourceTime) && @file_exists($cachedName) && (filemtime($cachedName) > $sourceTime)) {
- return true;
- } else {
- return false;
- }
- } // _isCached
-
-
- /**
- * Loads a "prepared" template file
- *
- * @access private
- * @param string filename
- * @param string block name
- * @param string variable placeholder to replace by a block
- * @return mixed SIGMA_OK on success, error object on failure
- * @see loadTemplatefile(), addBlockfile(), replaceBlockfile()
- */
- function _getCached($filename, $block = '__global__', $placeholder = '')
- {
- // the same checks are done in addBlock()
- if (!empty($placeholder)) {
- if (isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_EXISTS, $block), SIGMA_BLOCK_EXISTS);
- }
- $parents = $this->_findParentBlocks($placeholder);
- if (0 == count($parents)) {
- return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_NOT_FOUND, $placeholder), SIGMA_PLACEHOLDER_NOT_FOUND);
- } elseif (count($parents) > 1) {
- return $this->raiseError($this->errorMessage(SIGMA_PLACEHOLDER_DUPLICATE, $placeholder), SIGMA_PLACEHOLDER_DUPLICATE);
- }
- }
- $content = $this->_getFile($this->_cachedName($filename));
- if (PEAR::isError($content)) {
- return $content;
- }
- $cache = unserialize($content);
- if ('__global__' != $block) {
- $this->_blocks[$block] = $cache['blocks']['__global__'];
- $this->_blockVariables[$block] = $cache['variables']['__global__'];
- $this->_children[$block] = $cache['children']['__global__'];
- $this->_functions[$block] = $cache['functions']['__global__'];
- unset($cache['blocks']['__global__'], $cache['variables']['__global__'], $cache['children']['__global__'], $cache['functions']['__global__']);
- }
- $this->_blocks = array_merge($this->_blocks, $cache['blocks']);
- $this->_blockVariables = array_merge($this->_blockVariables, $cache['variables']);
- $this->_children = array_merge($this->_children, $cache['children']);
- $this->_functions = array_merge($this->_functions, $cache['functions']);
-
- // the same thing gets done in addBlockfile()
- if (!empty($placeholder)) {
- $this->_replacePlaceholder($parents[0], $placeholder, $block);
- }
- // pull the triggers, if any
- if (isset($cache['triggers'])) {
- return $this->_pullTriggers($cache['triggers']);
- }
- return SIGMA_OK;
- } // _getCached
-
-
- /**
- * Returns a full name of a "prepared" template file
- *
- * @access private
- * @param string source filename, relative to root directory
- * @return string filename
- */
- function _cachedName($filename)
- {
- if (OS_WINDOWS) {
- $filename = str_replace(array('/', '\\', ':'), array('__', '__', ''), $filename);
- } else {
- $filename = str_replace('/', '__', $filename);
- }
- return $this->_cacheRoot. $filename. '.it';
- } // _cachedName
-
-
- /**
- * Writes a prepared template file.
- *
- * Even if NO caching is going on, this method has a side effect: it calls
- * the _pullTriggers() method and thus loads all files added via <!-- INCLUDE -->
- *
- * @access private
- * @param string source filename, relative to root directory
- * @param string name of the block to save into file
- * @return mixed SIGMA_OK on success, error object on failure
- */
- function _writeCache($filename, $block)
- {
- // do not save anything if no cache dir, but do pull triggers
- if (null !== $this->_cacheRoot) {
- $cache = array(
- 'blocks' => array(),
- 'variables' => array(),
- 'children' => array(),
- 'functions' => array()
- );
- $cachedName = $this->_cachedName($filename);
- $this->_buildCache($cache, $block);
- if ('__global__' != $block) {
- foreach (array_keys($cache) as $k) {
- $cache[$k]['__global__'] = $cache[$k][$block];
- unset($cache[$k][$block]);
- }
- }
- if (isset($this->_triggers[$block])) {
- $cache['triggers'] = $this->_triggers[$block];
- }
- if (!($fh = @fopen($cachedName, 'wb'))) {
- return $this->raiseError($this->errorMessage(SIGMA_CACHE_ERROR, $cachedName), SIGMA_CACHE_ERROR);
- }
- fwrite($fh, serialize($cache));
- fclose($fh);
- }
- // now pull triggers
- if (isset($this->_triggers[$block])) {
- if (SIGMA_OK !== ($res = $this->_pullTriggers($this->_triggers[$block]))) {
- return $res;
- }
- unset($this->_triggers[$block]);
- }
- return SIGMA_OK;
- } // _writeCache
-
-
- /**
- * Builds an array of template data to be saved in prepared template file
- *
- * @access private
- * @param array template data
- * @param string block to add to the array
- */
- function _buildCache(&$cache, $block)
- {
- if (!$this->_options['trim_on_save']) {
- $cache['blocks'][$block] = $this->_blocks[$block];
- } else {
- $cache['blocks'][$block] = preg_replace(
- array('/^\\s+/m', '/\\s+$/m', '/(\\r?\\n)+/'),
- array('', '', "\n"),
- $this->_blocks[$block]
- );
- }
- $cache['variables'][$block] = $this->_blockVariables[$block];
- $cache['functions'][$block] = isset($this->_functions[$block])? $this->_functions[$block]: array();
- if (!isset($this->_children[$block])) {
- $cache['children'][$block] = array();
- } else {
- $cache['children'][$block] = $this->_children[$block];
- foreach (array_keys($this->_children[$block]) as $child) {
- $this->_buildCache($cache, $child);
- }
- }
- }
-
-
- /**
- * Recursively removes all data belonging to a block
- *
- * @param string block name
- * @param boolean true if the parsed contents of the block should be kept
- * @return mixed SIGMA_OK on success, error object on failure
- * @see replaceBlock(), replaceBlockfile()
- * @access private
- */
- function _removeBlockData($block, $keepContent = false)
- {
- if (!isset($this->_blocks[$block])) {
- return $this->raiseError($this->errorMessage(SIGMA_BLOCK_NOT_FOUND, $block), SIGMA_BLOCK_NOT_FOUND);
- }
- if (!empty($this->_children[$block])) {
- foreach (array_keys($this->_children[$block]) as $child) {
- $this->_removeBlockData($child, false);
- }
- unset($this->_children[$block]);
- }
- unset($this->_blocks[$block]);
- unset($this->_blockVariables[$block]);
- unset($this->_hiddenBlocks[$block]);
- unset($this->_touchedBlocks[$block]);
- unset($this->_functions[$block]);
- if (!$keepContent) {
- unset($this->_parsedBlocks[$block]);
- }
- return SIGMA_OK;
- }
-
-
- /**
- * Returns the names of the blocks where the variable placeholder appears
- *
- * @param string variable name
- * @return array block names
- * @see addBlock(), addBlockfile(), placeholderExists()
- * @access private
- */
- function _findParentBlocks($variable)
- {
- $parents = array();
- foreach ($this->_blockVariables as $blockname => $varnames) {
- if (!empty($varnames[$variable])) {
- $parents[] = $blockname;
- }
- }
- return $parents;
- }
-
-
- /**
- * Replaces a variable placeholder by a block placeholder.
- *
- * Of course, it also updates the necessary arrays
- *
- * @param string name of the block containing the placeholder
- * @param string variable name
- * @param string block name
- * @access private
- */
- function _replacePlaceholder($parent, $placeholder, $block)
- {
- $this->_children[$parent][$block] = true;
- $this->_blockVariables[$parent]['__'.$block.'__'] = true;
- $this->_blocks[$parent] = str_replace($this->openingDelimiter.$placeholder.$this->closingDelimiter,
- $this->openingDelimiter.'__'.$block.'__'.$this->closingDelimiter,
- $this->_blocks[$parent] );
- unset($this->_blockVariables[$parent][$placeholder]);
- }
-
-
- /**
- * Generates a placeholder to replace an <!-- INCLUDE filename --> statement
- *
- * @access private
- * @param string filename
- * @param string current block name
- * @return string a placeholder
- */
- function _makeTrigger($filename, $block)
- {
- $name = 'trigger_' . substr(md5($filename . ' ' . uniqid($block)), 0, 10);
- $this->_triggers[$block][$name] = $filename;
- return $this->openingDelimiter . $name . $this->closingDelimiter;
- }
-
-
- /**
- * Replaces the "trigger" placeholders by the matching file contents.
- *
- * @see _makeTrigger(), addBlockfile()
- * @param array array ('trigger placeholder' => 'filename')
- * @return mixed SIGMA_OK on success, error object on failure
- * @access private
- */
- function _pullTriggers($triggers)
- {
- foreach ($triggers as $placeholder => $filename) {
- if (SIGMA_OK !== ($res = $this->addBlockfile($placeholder, $placeholder, $filename))) {
- return $res;
- }
- // we actually do not need the resultant block...
- $parents = $this->_findParentBlocks('__' . $placeholder . '__');
- // merge current block's children and variables with the parent's ones
- if (isset($this->_children[$placeholder])) {
- $this->_children[$parents[0]] = array_merge($this->_children[$parents[0]], $this->_children[$placeholder]);
- }
- $this->_blockVariables[$parents[0]] = array_merge($this->_blockVariables[$parents[0]], $this->_blockVariables[$placeholder]);
- if (isset($this->_functions[$placeholder])) {
- $this->_functions[$parents[0]] = array_merge($this->_functions[$parents[0]], $this->_functions[$placeholder]);
- }
- // substitute the block's contents into parent's
- $this->_blocks[$parents[0]] = str_replace(
- $this->openingDelimiter . '__' . $placeholder . '__' . $this->closingDelimiter,
- $this->_blocks[$placeholder],
- $this->_blocks[$parents[0]]
- );
- // remove the stuff that is no more needed
- unset($this->_blocks[$placeholder], $this->_blockVariables[$placeholder], $this->_children[$placeholder], $this->_functions[$placeholder]);
- unset($this->_children[$parents[0]][$placeholder], $this->_blockVariables[$parents[0]]['__' . $placeholder . '__']);
- }
- return SIGMA_OK;
- }
-
-
- /**
- * Builds a list of functions in a block.
- *
- * @access private
- * @param string Block name
- * @see _buildBlockVariables()
- */
- function _buildFunctionlist($block)
- {
- $template = $this->_blocks[$block];
- $this->_blocks[$block] = '';
-
- while (preg_match($this->functionRegExp, $template, $regs)) {
- $this->_blocks[$block] .= substr($template, 0, strpos($template, $regs[0]));
- $template = substr($template, strpos($template, $regs[0]) + strlen($regs[0]));
-
- $state = 1;
- $funcData = array(
- 'name' => $regs[1],
- 'args' => array()
- );
- for ($i = 0, $len = strlen($template); $i < $len; $i++) {
- $char = $template{$i};
- switch ($state) {
- case 0:
- case -1:
- break 2;
-
- case 1:
- $arg = '';
- if (')' == $char) {
- $state = 0;
- } elseif (',' == $char) {
- $error = 'Unexpected \',\'';
- $state = -1;
- } elseif ('\'' == $char || '"' == $char) {
- $quote = $char;
- $state = 5;
- } elseif (!ctype_space($char)) {
- $arg .= $char;
- $state = 3;
- }
- break;
-
- case 2:
- $arg = '';
- if (',' == $char || ')' == $char) {
- $error = 'Unexpected \'' . $char . '\'';
- $state = -1;
- } elseif ('\'' == $char || '"' == $char) {
- $quote = $char;
- $state = 5;
- } elseif (!ctype_space($char)) {
- $arg .= $char;
- $state = 3;
- }
- break;
-
- case 3:
- if (')' == $char) {
- $funcData['args'][] = rtrim($arg);
- $state = 0;
- } elseif (',' == $char) {
- $funcData['args'][] = rtrim($arg);
- $state = 2;
- } elseif ('\'' == $char || '"' == $char) {
- $quote = $char;
- $arg .= $char;
- $state = 4;
- } else {
- $arg .= $char;
- }
- break;
-
- case 4:
- $arg .= $char;
- if ($quote == $char) {
- $state = 3;
- }
- break;
-
- case 5:
- if ('\\' == $char) {
- $state = 6;
- } elseif ($quote == $char) {
- $state = 7;
- } else {
- $arg .= $char;
- }
- break;
-
- case 6:
- $arg .= $char;
- $state = 5;
- break;
-
- case 7:
- if (')' == $char) {
- $funcData['args'][] = $arg;
- $state = 0;
- } elseif (',' == $char) {
- $funcData['args'][] = $arg;
- $state = 2;
- } elseif (!ctype_space($char)) {
- $error = 'Unexpected \'' . $char . '\' (expected: \')\' or \',\')';
- $state = -1;
- }
- break;
- } // switch
- } // for
- if (0 != $state) {
- return $this->raiseError($this->errorMessage(SIGMA_CALLBACK_SYNTAX_ERROR, (empty($error)? 'Unexpected end of input': $error) . ' in ' . $regs[0] . substr($template, 0, $i)), SIGMA_CALLBACK_SYNTAX_ERROR);
- } else {
- $funcId = 'f' . substr(md5(serialize($funcData)), 0, 10);
- $template = substr($template, $i);
-
- $this->_blocks[$block] .= $this->openingDelimiter . '__function_' . $funcId . '__' . $this->closingDelimiter;
- $this->_blockVariables[$block]['__function_' . $funcId . '__'] = true;
- $this->_functions[$block][$funcId] = $funcData;
- }
- } // while
- $this->_blocks[$block] .= $template;
- return SIGMA_OK;
- } // end func _buildFunctionlist
-
-
- /**
- * Replaces an opening delimiter by a special string.
- *
- * Used to implement $_options['preserve_data'] logic
- *
- * @access private
- * @param string
- * @return string
- */
- function _preserveOpeningDelimiter($str)
- {
- return (false === strpos($str, $this->openingDelimiter))?
- $str:
- str_replace($this->openingDelimiter, $this->openingDelimiter . '%preserved%' . $this->closingDelimiter, $str);
- }
-
-
- /**
- * Quotes the string so that it can be used in Javascript string constants
- *
- * @access private
- * @param string
- * @return string
- */
- function _jsEscape($value)
- {
- return strtr($value, array(
- "\r" => '\r', "'" => "\\'", "\n" => '\n',
- '"' => '\"', "\t" => '\t', '\\' => '\\\\'
- ));
- }
- }
- ?>
-