You are here

class Smarty_Compiler in Quiz 6.6

Same name and namespace in other branches
  1. 6.5 includes/moodle/lib/smarty/Smarty_Compiler.class.php \Smarty_Compiler

Template compiling class @package Smarty

Hierarchy

Expanded class hierarchy of Smarty_Compiler

File

includes/moodle/lib/smarty/Smarty_Compiler.class.php, line 34

View source
class Smarty_Compiler extends Smarty {

  // internal vars

  /**#@+
   * @access private
   */
  var $_folded_blocks = array();

  // keeps folded template blocks
  var $_current_file = null;

  // the current template being compiled
  var $_current_line_no = 1;

  // line number for error messages
  var $_capture_stack = array();

  // keeps track of nested capture buffers
  var $_plugin_info = array();

  // keeps track of plugins to load
  var $_init_smarty_vars = false;
  var $_permitted_tokens = array(
    'true',
    'false',
    'yes',
    'no',
    'on',
    'off',
    'null',
  );
  var $_db_qstr_regexp = null;

  // regexps are setup in the constructor
  var $_si_qstr_regexp = null;
  var $_qstr_regexp = null;
  var $_func_regexp = null;
  var $_reg_obj_regexp = null;
  var $_var_bracket_regexp = null;
  var $_num_const_regexp = null;
  var $_dvar_guts_regexp = null;
  var $_dvar_regexp = null;
  var $_cvar_regexp = null;
  var $_svar_regexp = null;
  var $_avar_regexp = null;
  var $_mod_regexp = null;
  var $_var_regexp = null;
  var $_parenth_param_regexp = null;
  var $_func_call_regexp = null;
  var $_obj_ext_regexp = null;
  var $_obj_start_regexp = null;
  var $_obj_params_regexp = null;
  var $_obj_call_regexp = null;
  var $_cacheable_state = 0;
  var $_cache_attrs_count = 0;
  var $_nocache_count = 0;
  var $_cache_serial = null;
  var $_cache_include = null;
  var $_strip_depth = 0;
  var $_additional_newline = "\n";

  /**#@-*/

  /**
   * The class constructor.
   */
  function Smarty_Compiler() {

    // matches double quoted strings:
    // "foobar"
    // "foo\"bar"
    $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';

    // matches single quoted strings:
    // 'foobar'
    // 'foo\'bar'
    $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';

    // matches single or double quoted strings
    $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';

    // matches bracket portion of vars
    // [0]
    // [foo]
    // [$bar]
    $this->_var_bracket_regexp = '\\[\\$?[\\w\\.]+\\]';

    // matches numerical constants
    // 30
    // -12
    // 13.22
    $this->_num_const_regexp = '(?:\\-?\\d+(?:\\.\\d+)?)';

    // matches $ vars (not objects):
    // $foo
    // $foo.bar
    // $foo.bar.foobar
    // $foo[0]
    // $foo[$bar]
    // $foo[5][blah]
    // $foo[5].bar[$foobar][4]
    $this->_dvar_math_regexp = '(?:[\\+\\*\\/\\%]|(?:-(?!>)))';
    $this->_dvar_math_var_regexp = '[\\$\\w\\.\\+\\-\\*\\/\\%\\d\\>\\[\\]]';
    $this->_dvar_guts_regexp = '\\w+(?:' . $this->_var_bracket_regexp . ')*(?:\\.\\$?\\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
    $this->_dvar_regexp = '\\$' . $this->_dvar_guts_regexp;

    // matches config vars:
    // #foo#
    // #foobar123_foo#
    $this->_cvar_regexp = '\\#\\w+\\#';

    // matches section vars:
    // %foo.bar%
    $this->_svar_regexp = '\\%\\w+\\.\\w+\\%';

    // matches all valid variables (no quotes, no modifiers)
    $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';

    // matches valid variable syntax:
    // $foo
    // $foo
    // #foo#
    // #foo#
    // "text"
    // "text"
    $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';

    // matches valid object call (one level of object nesting allowed in parameters):
    // $foo->bar
    // $foo->bar()
    // $foo->bar("text")
    // $foo->bar($foo, $bar, "text")
    // $foo->bar($foo, "foo")
    // $foo->bar->foo()
    // $foo->bar->foo->bar()
    // $foo->bar($foo->bar)
    // $foo->bar($foo->bar())
    // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
    $this->_obj_ext_regexp = '\\->(?:\\$?' . $this->_dvar_guts_regexp . ')';
    $this->_obj_restricted_param_regexp = '(?:' . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')' . '(?:\\s*,\\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\\))?)*)';
    $this->_obj_single_param_regexp = '(?:\\w+|' . $this->_obj_restricted_param_regexp . '(?:\\s*,\\s*(?:(?:\\w+|' . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
    $this->_obj_params_regexp = '\\((?:' . $this->_obj_single_param_regexp . '(?:\\s*,\\s*' . $this->_obj_single_param_regexp . ')*)?\\)';
    $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
    $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';

    // matches valid modifier syntax:
    // |foo
    // |@foo
    // |foo:"bar"
    // |foo:$bar
    // |foo:"bar":$foobar
    // |foo|bar
    // |foo:$foo->bar
    $this->_mod_regexp = '(?:\\|@?\\w+(?::(?:\\w+|' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp . '))*)';

    // matches valid function name:
    // foo123
    // _foo_bar
    $this->_func_regexp = '[a-zA-Z_]\\w*';

    // matches valid registered object:
    // foo->bar
    $this->_reg_obj_regexp = '[a-zA-Z_]\\w*->[a-zA-Z_]\\w*';

    // matches valid parameter values:
    // true
    // $foo
    // $foo|bar
    // #foo#
    // #foo#|bar
    // "text"
    // "text"|bar
    // $foo->bar
    $this->_param_regexp = '(?:\\s*(?:' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\\w+)(?>' . $this->_mod_regexp . '*)\\s*)';

    // matches valid parenthesised function parameters:
    //
    // "text"
    //    $foo, $bar, "text"
    // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
    $this->_parenth_param_regexp = '(?:\\((?:\\w+|' . $this->_param_regexp . '(?:\\s*,\\s*(?:(?:\\w+|' . $this->_param_regexp . ')))*)?\\))';

    // matches valid function call:
    // foo()
    // foo_bar($foo)
    // _foo_bar($foo,"bar")
    // foo123($foo,$foo->bar(),"foo")
    $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\\s*(?:' . $this->_parenth_param_regexp . '))';
  }

  /**
   * compile a resource
   *
   * sets $compiled_content to the compiled source
   * @param string $resource_name
   * @param string $source_content
   * @param string $compiled_content
   * @return true
   */
  function _compile_file($resource_name, $source_content, &$compiled_content) {
    if ($this->security) {

      // do not allow php syntax to be executed unless specified
      if ($this->php_handling == SMARTY_PHP_ALLOW && !$this->security_settings['PHP_HANDLING']) {
        $this->php_handling = SMARTY_PHP_PASSTHRU;
      }
    }
    $this
      ->_load_filters();
    $this->_current_file = $resource_name;
    $this->_current_line_no = 1;
    $ldq = preg_quote($this->left_delimiter, '~');
    $rdq = preg_quote($this->right_delimiter, '~');

    // run template source through prefilter functions
    if (count($this->_plugins['prefilter']) > 0) {
      foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
        if ($prefilter === false) {
          continue;
        }
        if ($prefilter[3] || is_callable($prefilter[0])) {
          $source_content = call_user_func_array($prefilter[0], array(
            $source_content,
            &$this,
          ));
          $this->_plugins['prefilter'][$filter_name][3] = true;
        }
        else {
          $this
            ->_trigger_fatal_error("[plugin] prefilter '{$filter_name}' is not implemented");
        }
      }
    }

    /* fetch all special blocks */
    $search = "~{$ldq}\\*(.*?)\\*{$rdq}|{$ldq}\\s*literal\\s*{$rdq}(.*?){$ldq}\\s*/literal\\s*{$rdq}|{$ldq}\\s*php\\s*{$rdq}(.*?){$ldq}\\s*/php\\s*{$rdq}~s";
    preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
    $this->_folded_blocks = $match;
    reset($this->_folded_blocks);

    /* replace special blocks by "{php}" */
    $source_content = preg_replace($search . 'e', "'" . $this
      ->_quote_replace($this->left_delimiter) . 'php' . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'" . $this
      ->_quote_replace($this->right_delimiter) . "'", $source_content);

    /* Gather all template tags. */
    preg_match_all("~{$ldq}\\s*(.*?)\\s*{$rdq}~s", $source_content, $_match);
    $template_tags = $_match[1];

    /* Split content by template tags to obtain non-template content. */
    $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);

    /* loop through text blocks */
    for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {

      /* match anything resembling php tags */
      if (preg_match_all('~(<\\?(?:\\w+|=)?|\\?>|language\\s*=\\s*[\\"\']?php[\\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {

        /* replace tags with placeholders to prevent recursive replacements */
        $sp_match[1] = array_unique($sp_match[1]);
        usort($sp_match[1], '_smarty_sort_length');
        for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
          $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp], '%%%SMARTYSP' . $curr_sp . '%%%', $text_blocks[$curr_tb]);
        }

        /* process each one */
        for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
          if ($this->php_handling == SMARTY_PHP_PASSTHRU) {

            /* echo php contents */
            $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP' . $curr_sp . '%%%', '<?php echo \'' . str_replace("'", "\\'", $sp_match[1][$curr_sp]) . '\'; ?>' . "\n", $text_blocks[$curr_tb]);
          }
          else {
            if ($this->php_handling == SMARTY_PHP_QUOTE) {

              /* quote php tags */
              $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP' . $curr_sp . '%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
            }
            else {
              if ($this->php_handling == SMARTY_PHP_REMOVE) {

                /* remove php tags */
                $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP' . $curr_sp . '%%%', '', $text_blocks[$curr_tb]);
              }
              else {

                /* SMARTY_PHP_ALLOW, but echo non php starting tags */
                $sp_match[1][$curr_sp] = preg_replace('~(<\\?(?!php|=|$))~i', '<?php echo \'\\1\'?>' . "\n", $sp_match[1][$curr_sp]);
                $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP' . $curr_sp . '%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
              }
            }
          }
        }
      }
    }

    /* Compile the template tags into PHP code. */
    $compiled_tags = array();
    for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
      $this->_current_line_no += substr_count($text_blocks[$i], "\n");
      $compiled_tags[] = $this
        ->_compile_tag($template_tags[$i]);
      $this->_current_line_no += substr_count($template_tags[$i], "\n");
    }
    if (count($this->_tag_stack) > 0) {
      list($_open_tag, $_line_no) = end($this->_tag_stack);
      $this
        ->_syntax_error("unclosed tag \\{{$_open_tag}} (opened line {$_line_no}).", E_USER_ERROR, __FILE__, __LINE__);
      return;
    }

    /* Reformat $text_blocks between 'strip' and '/strip' tags,
       removing spaces, tabs and newlines. */
    $strip = false;
    for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
      if ($compiled_tags[$i] == '{strip}') {
        $compiled_tags[$i] = '';
        $strip = true;

        /* remove leading whitespaces */
        $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
      }
      if ($strip) {

        /* strip all $text_blocks before the next '/strip' */
        for ($j = $i + 1; $j < $for_max; $j++) {

          /* remove leading and trailing whitespaces of each line */
          $text_blocks[$j] = preg_replace('![\\t ]*[\\r\\n]+[\\t ]*!', '', $text_blocks[$j]);
          if ($compiled_tags[$j] == '{/strip}') {

            /* remove trailing whitespaces from the last text_block */
            $text_blocks[$j] = rtrim($text_blocks[$j]);
          }
          $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array(
            "'" => "\\'",
            "\\" => "\\\\",
          )) . "'; ?>";
          if ($compiled_tags[$j] == '{/strip}') {
            $compiled_tags[$j] = "\n";

            /* slurped by php, but necessary
               if a newline is following the closing strip-tag */
            $strip = false;
            $i = $j;
            break;
          }
        }
      }
    }
    $compiled_content = '';

    /* Interleave the compiled contents and text blocks to get the final result. */
    for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
      if ($compiled_tags[$i] == '') {

        // tag result empty, remove first newline from following text block
        $text_blocks[$i + 1] = preg_replace('~^(\\r\\n|\\r|\\n)~', '', $text_blocks[$i + 1]);
      }
      $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
    }
    $compiled_content .= $text_blocks[$i];

    // remove \n from the end of the file, if any
    if (($_len = strlen($compiled_content)) && $compiled_content[$_len - 1] == "\n") {
      $compiled_content = substr($compiled_content, 0, -1);
    }
    if (!empty($this->_cache_serial)) {
      $compiled_content = "<?php \$this->_cache_serials['" . $this->_cache_include . "'] = '" . $this->_cache_serial . "'; ?>" . $compiled_content;
    }

    // remove unnecessary close/open tags
    $compiled_content = preg_replace('~\\?>\\n?<\\?php~', '', $compiled_content);

    // run compiled template through postfilter functions
    if (count($this->_plugins['postfilter']) > 0) {
      foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
        if ($postfilter === false) {
          continue;
        }
        if ($postfilter[3] || is_callable($postfilter[0])) {
          $compiled_content = call_user_func_array($postfilter[0], array(
            $compiled_content,
            &$this,
          ));
          $this->_plugins['postfilter'][$filter_name][3] = true;
        }
        else {
          $this
            ->_trigger_fatal_error("Smarty plugin error: postfilter '{$filter_name}' is not implemented");
        }
      }
    }

    // put header at the top of the compiled template
    $template_header = "<?php /* Smarty version " . $this->_version . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n";
    $template_header .= "         compiled from " . strtr(urlencode($resource_name), array(
      '%2F' => '/',
      '%3A' => ':',
    )) . " */ ?>\n";

    /* Emit code to load needed plugins. */
    $this->_plugins_code = '';
    if (count($this->_plugin_info)) {
      $_plugins_params = "array('plugins' => array(";
      foreach ($this->_plugin_info as $plugin_type => $plugins) {
        foreach ($plugins as $plugin_name => $plugin_info) {
          $_plugins_params .= "array('{$plugin_type}', '{$plugin_name}', '" . strtr($plugin_info[0], array(
            "'" => "\\'",
            "\\" => "\\\\",
          )) . "', {$plugin_info[1]}, ";
          $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
        }
      }
      $_plugins_params .= '))';
      $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins({$_plugins_params}, \$this); ?>\n";
      $template_header .= $plugins_code;
      $this->_plugin_info = array();
      $this->_plugins_code = $plugins_code;
    }
    if ($this->_init_smarty_vars) {
      $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
      $this->_init_smarty_vars = false;
    }
    $compiled_content = $template_header . $compiled_content;
    return true;
  }

  /**
   * Compile a template tag
   *
   * @param string $template_tag
   * @return string
   */
  function _compile_tag($template_tag) {

    /* Matched comment. */
    if ($template_tag[0] == '*' && $template_tag[strlen($template_tag) - 1] == '*') {
      return '';
    }

    /* Split tag into two three parts: command, command modifiers and the arguments. */
    if (!preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '|\\/?' . $this->_reg_obj_regexp . '|\\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
                      (?:\\s+(.*))?$
                    ~xs', $template_tag, $match)) {
      $this
        ->_syntax_error("unrecognized tag: {$template_tag}", E_USER_ERROR, __FILE__, __LINE__);
    }
    $tag_command = $match[1];
    $tag_modifier = isset($match[2]) ? $match[2] : null;
    $tag_args = isset($match[3]) ? $match[3] : null;
    if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {

      /* tag name is a variable or object */
      $_return = $this
        ->_parse_var_props($tag_command . $tag_modifier);
      return "<?php echo {$_return}; ?>" . $this->_additional_newline;
    }

    /* If the tag name is a registered object, we process it. */
    if (preg_match('~^\\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
      return $this
        ->_compile_registered_object_tag($tag_command, $this
        ->_parse_attrs($tag_args), $tag_modifier);
    }
    switch ($tag_command) {
      case 'include':
        return $this
          ->_compile_include_tag($tag_args);
      case 'include_php':
        return $this
          ->_compile_include_php_tag($tag_args);
      case 'if':
        $this
          ->_push_tag('if');
        return $this
          ->_compile_if_tag($tag_args);
      case 'else':
        list($_open_tag) = end($this->_tag_stack);
        if ($_open_tag != 'if' && $_open_tag != 'elseif') {
          $this
            ->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
        }
        else {
          $this
            ->_push_tag('else');
        }
        return '<?php else: ?>';
      case 'elseif':
        list($_open_tag) = end($this->_tag_stack);
        if ($_open_tag != 'if' && $_open_tag != 'elseif') {
          $this
            ->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
        }
        if ($_open_tag == 'if') {
          $this
            ->_push_tag('elseif');
        }
        return $this
          ->_compile_if_tag($tag_args, true);
      case '/if':
        $this
          ->_pop_tag('if');
        return '<?php endif; ?>';
      case 'capture':
        return $this
          ->_compile_capture_tag(true, $tag_args);
      case '/capture':
        return $this
          ->_compile_capture_tag(false);
      case 'ldelim':
        return $this->left_delimiter;
      case 'rdelim':
        return $this->right_delimiter;
      case 'section':
        $this
          ->_push_tag('section');
        return $this
          ->_compile_section_start($tag_args);
      case 'sectionelse':
        $this
          ->_push_tag('sectionelse');
        return "<?php endfor; else: ?>";
        break;
      case '/section':
        $_open_tag = $this
          ->_pop_tag('section');
        if ($_open_tag == 'sectionelse') {
          return "<?php endif; ?>";
        }
        else {
          return "<?php endfor; endif; ?>";
        }
      case 'foreach':
        $this
          ->_push_tag('foreach');
        return $this
          ->_compile_foreach_start($tag_args);
        break;
      case 'foreachelse':
        $this
          ->_push_tag('foreachelse');
        return "<?php endforeach; else: ?>";
      case '/foreach':
        $_open_tag = $this
          ->_pop_tag('foreach');
        if ($_open_tag == 'foreachelse') {
          return "<?php endif; unset(\$_from); ?>";
        }
        else {
          return "<?php endforeach; endif; unset(\$_from); ?>";
        }
        break;
      case 'strip':
      case '/strip':
        if ($tag_command[0] == '/') {
          $this
            ->_pop_tag('strip');
          if (--$this->_strip_depth == 0) {

            /* outermost closing {/strip} */
            $this->_additional_newline = "\n";
            return '{' . $tag_command . '}';
          }
        }
        else {
          $this
            ->_push_tag('strip');
          if ($this->_strip_depth++ == 0) {

            /* outermost opening {strip} */
            $this->_additional_newline = "";
            return '{' . $tag_command . '}';
          }
        }
        return '';
      case 'php':

        /* handle folded tags replaced by {php} */
        list(, $block) = each($this->_folded_blocks);
        $this->_current_line_no += substr_count($block[0], "\n");

        /* the number of matched elements in the regexp in _compile_file()
           determins the type of folded tag that was found */
        switch (count($block)) {
          case 2:

            /* comment */
            return '';
          case 3:

            /* literal */
            return "<?php echo '" . strtr($block[2], array(
              "'" => "\\'",
              "\\" => "\\\\",
            )) . "'; ?>" . $this->_additional_newline;
          case 4:

            /* php */
            if ($this->security && !$this->security_settings['PHP_TAGS']) {
              $this
                ->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
              return;
            }
            return '<?php ' . $block[3] . ' ?>';
        }
        break;
      case 'insert':
        return $this
          ->_compile_insert_tag($tag_args);
      default:
        if ($this
          ->_compile_compiler_tag($tag_command, $tag_args, $output)) {
          return $output;
        }
        else {
          if ($this
            ->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
            return $output;
          }
          else {
            if ($this
              ->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
              return $output;
            }
            else {
              $this
                ->_syntax_error("unrecognized tag '{$tag_command}'", E_USER_ERROR, __FILE__, __LINE__);
            }
          }
        }
    }
  }

  /**
   * compile the custom compiler tag
   *
   * sets $output to the compiled custom compiler tag
   * @param string $tag_command
   * @param string $tag_args
   * @param string $output
   * @return boolean
   */
  function _compile_compiler_tag($tag_command, $tag_args, &$output) {
    $found = false;
    $have_function = true;

    /*
     * First we check if the compiler function has already been registered
     * or loaded from a plugin file.
     */
    if (isset($this->_plugins['compiler'][$tag_command])) {
      $found = true;
      $plugin_func = $this->_plugins['compiler'][$tag_command][0];
      if (!is_callable($plugin_func)) {
        $message = "compiler function '{$tag_command}' is not implemented";
        $have_function = false;
      }
    }
    else {
      if ($plugin_file = $this
        ->_get_plugin_filepath('compiler', $tag_command)) {
        $found = true;
        include_once $plugin_file;
        $plugin_func = 'smarty_compiler_' . $tag_command;
        if (!is_callable($plugin_func)) {
          $message = "plugin function {$plugin_func}() not found in {$plugin_file}\n";
          $have_function = false;
        }
        else {
          $this->_plugins['compiler'][$tag_command] = array(
            $plugin_func,
            null,
            null,
            null,
            true,
          );
        }
      }
    }

    /*
     * True return value means that we either found a plugin or a
     * dynamically registered function. False means that we didn't and the
     * compiler should now emit code to load custom function plugin for this
     * tag.
     */
    if ($found) {
      if ($have_function) {
        $output = call_user_func_array($plugin_func, array(
          $tag_args,
          &$this,
        ));
        if ($output != '') {
          $output = '<?php ' . $this
            ->_push_cacheable_state('compiler', $tag_command) . $output . $this
            ->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
        }
      }
      else {
        $this
          ->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
      }
      return true;
    }
    else {
      return false;
    }
  }

  /**
   * compile block function tag
   *
   * sets $output to compiled block function tag
   * @param string $tag_command
   * @param string $tag_args
   * @param string $tag_modifier
   * @param string $output
   * @return boolean
   */
  function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output) {
    if ($tag_command[0] == '/') {
      $start_tag = false;
      $tag_command = substr($tag_command, 1);
    }
    else {
      $start_tag = true;
    }
    $found = false;
    $have_function = true;

    /*
     * First we check if the block function has already been registered
     * or loaded from a plugin file.
     */
    if (isset($this->_plugins['block'][$tag_command])) {
      $found = true;
      $plugin_func = $this->_plugins['block'][$tag_command][0];
      if (!is_callable($plugin_func)) {
        $message = "block function '{$tag_command}' is not implemented";
        $have_function = false;
      }
    }
    else {
      if ($plugin_file = $this
        ->_get_plugin_filepath('block', $tag_command)) {
        $found = true;
        include_once $plugin_file;
        $plugin_func = 'smarty_block_' . $tag_command;
        if (!function_exists($plugin_func)) {
          $message = "plugin function {$plugin_func}() not found in {$plugin_file}\n";
          $have_function = false;
        }
        else {
          $this->_plugins['block'][$tag_command] = array(
            $plugin_func,
            null,
            null,
            null,
            true,
          );
        }
      }
    }
    if (!$found) {
      return false;
    }
    else {
      if (!$have_function) {
        $this
          ->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
        return true;
      }
    }

    /*
     * Even though we've located the plugin function, compilation
     * happens only once, so the plugin will still need to be loaded
     * at runtime for future requests.
     */
    $this
      ->_add_plugin('block', $tag_command);
    if ($start_tag) {
      $this
        ->_push_tag($tag_command);
    }
    else {
      $this
        ->_pop_tag($tag_command);
    }
    if ($start_tag) {
      $output = '<?php ' . $this
        ->_push_cacheable_state('block', $tag_command);
      $attrs = $this
        ->_parse_attrs($tag_args);
      $arg_list = $this
        ->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs = '');
      $output .= "{$_cache_attrs}\$this->_tag_stack[] = array('{$tag_command}', array(" . implode(',', $arg_list) . ')); ';
      $output .= $this
        ->_compile_plugin_call('block', $tag_command) . '($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat=true);';
      $output .= 'while ($_block_repeat) { ob_start(); ?>';
    }
    else {
      $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
      $_out_tag_text = $this
        ->_compile_plugin_call('block', $tag_command) . '($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat=false)';
      if ($tag_modifier != '') {
        $this
          ->_parse_modifiers($_out_tag_text, $tag_modifier);
      }
      $output .= 'echo ' . $_out_tag_text . '; } ';
      $output .= " array_pop(\$this->_tag_stack); " . $this
        ->_pop_cacheable_state('block', $tag_command) . '?>';
    }
    return true;
  }

  /**
   * compile custom function tag
   *
   * @param string $tag_command
   * @param string $tag_args
   * @param string $tag_modifier
   * @return string
   */
  function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output) {
    $found = false;
    $have_function = true;

    /*
     * First we check if the custom function has already been registered
     * or loaded from a plugin file.
     */
    if (isset($this->_plugins['function'][$tag_command])) {
      $found = true;
      $plugin_func = $this->_plugins['function'][$tag_command][0];
      if (!is_callable($plugin_func)) {
        $message = "custom function '{$tag_command}' is not implemented";
        $have_function = false;
      }
    }
    else {
      if ($plugin_file = $this
        ->_get_plugin_filepath('function', $tag_command)) {
        $found = true;
        include_once $plugin_file;
        $plugin_func = 'smarty_function_' . $tag_command;
        if (!function_exists($plugin_func)) {
          $message = "plugin function {$plugin_func}() not found in {$plugin_file}\n";
          $have_function = false;
        }
        else {
          $this->_plugins['function'][$tag_command] = array(
            $plugin_func,
            null,
            null,
            null,
            true,
          );
        }
      }
    }
    if (!$found) {
      return false;
    }
    else {
      if (!$have_function) {
        $this
          ->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
        return true;
      }
    }

    /* declare plugin to be loaded on display of the template that
       we compile right now */
    $this
      ->_add_plugin('function', $tag_command);
    $_cacheable_state = $this
      ->_push_cacheable_state('function', $tag_command);
    $attrs = $this
      ->_parse_attrs($tag_args);
    $arg_list = $this
      ->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs = '');
    $output = $this
      ->_compile_plugin_call('function', $tag_command) . '(array(' . implode(',', $arg_list) . "), \$this)";
    if ($tag_modifier != '') {
      $this
        ->_parse_modifiers($output, $tag_modifier);
    }
    if ($output != '') {
      $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';' . $this
        ->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
    }
    return true;
  }

  /**
   * compile a registered object tag
   *
   * @param string $tag_command
   * @param array $attrs
   * @param string $tag_modifier
   * @return string
   */
  function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier) {
    if ($tag_command[0] == '/') {
      $start_tag = false;
      $tag_command = substr($tag_command, 1);
    }
    else {
      $start_tag = true;
    }
    list($object, $obj_comp) = explode('->', $tag_command);
    $arg_list = array();
    if (count($attrs)) {
      $_assign_var = false;
      foreach ($attrs as $arg_name => $arg_value) {
        if ($arg_name == 'assign') {
          $_assign_var = $arg_value;
          unset($attrs['assign']);
          continue;
        }
        if (is_bool($arg_value)) {
          $arg_value = $arg_value ? 'true' : 'false';
        }
        $arg_list[] = "'{$arg_name}' => {$arg_value}";
      }
    }
    if ($this->_reg_objects[$object][2]) {

      // smarty object argument format
      $args = "array(" . implode(',', (array) $arg_list) . "), \$this";
    }
    else {

      // traditional argument format
      $args = implode(',', array_values($attrs));
      if (empty($args)) {
        $args = 'null';
      }
    }
    $prefix = '';
    $postfix = '';
    $newline = '';
    if (!is_object($this->_reg_objects[$object][0])) {
      $this
        ->_trigger_fatal_error("registered '{$object}' is not an object", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
    }
    elseif (!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
      $this
        ->_trigger_fatal_error("'{$obj_comp}' is not a registered component of object '{$object}'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
    }
    elseif (method_exists($this->_reg_objects[$object][0], $obj_comp)) {

      // method
      if (in_array($obj_comp, $this->_reg_objects[$object][3])) {

        // block method
        if ($start_tag) {
          $prefix = "\$this->_tag_stack[] = array('{$obj_comp}', {$args}); ";
          $prefix .= "\$this->_reg_objects['{$object}'][0]->{$obj_comp}(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
          $prefix .= "while (\$_block_repeat) { ob_start();";
          $return = null;
          $postfix = '';
        }
        else {
          $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); ";
          $return = "\$this->_reg_objects['{$object}'][0]->{$obj_comp}(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat=false)";
          $postfix = "} array_pop(\$this->_tag_stack);";
        }
      }
      else {

        // non-block method
        $return = "\$this->_reg_objects['{$object}'][0]->{$obj_comp}({$args})";
      }
    }
    else {

      // property
      $return = "\$this->_reg_objects['{$object}'][0]->{$obj_comp}";
    }
    if ($return != null) {
      if ($tag_modifier != '') {
        $this
          ->_parse_modifiers($return, $tag_modifier);
      }
      if (!empty($_assign_var)) {
        $output = "\$this->assign('" . $this
          ->_dequote($_assign_var) . "',  {$return});";
      }
      else {
        $output = 'echo ' . $return . ';';
        $newline = $this->_additional_newline;
      }
    }
    else {
      $output = '';
    }
    return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
  }

  /**
   * Compile {insert ...} tag
   *
   * @param string $tag_args
   * @return string
   */
  function _compile_insert_tag($tag_args) {
    $attrs = $this
      ->_parse_attrs($tag_args);
    $name = $this
      ->_dequote($attrs['name']);
    if (empty($name)) {
      $this
        ->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
    }
    if (!empty($attrs['script'])) {
      $delayed_loading = true;
    }
    else {
      $delayed_loading = false;
    }
    foreach ($attrs as $arg_name => $arg_value) {
      if (is_bool($arg_value)) {
        $arg_value = $arg_value ? 'true' : 'false';
      }
      $arg_list[] = "'{$arg_name}' => {$arg_value}";
    }
    $this
      ->_add_plugin('insert', $name, $delayed_loading);
    $_params = "array('args' => array(" . implode(', ', (array) $arg_list) . "))";
    return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler({$_params}, \$this); ?>" . $this->_additional_newline;
  }

  /**
   * Compile {include ...} tag
   *
   * @param string $tag_args
   * @return string
   */
  function _compile_include_tag($tag_args) {
    $attrs = $this
      ->_parse_attrs($tag_args);
    $arg_list = array();
    if (empty($attrs['file'])) {
      $this
        ->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
    }
    foreach ($attrs as $arg_name => $arg_value) {
      if ($arg_name == 'file') {
        $include_file = $arg_value;
        continue;
      }
      else {
        if ($arg_name == 'assign') {
          $assign_var = $arg_value;
          continue;
        }
      }
      if (is_bool($arg_value)) {
        $arg_value = $arg_value ? 'true' : 'false';
      }
      $arg_list[] = "'{$arg_name}' => {$arg_value}";
    }
    $output = '<?php ';
    if (isset($assign_var)) {
      $output .= "ob_start();\n";
    }
    $output .= "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
    $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(" . implode(',', (array) $arg_list) . "))";
    $output .= "\$this->_smarty_include({$_params});\n" . "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" . "unset(\$_smarty_tpl_vars);\n";
    if (isset($assign_var)) {
      $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
    }
    $output .= ' ?>';
    return $output;
  }

  /**
   * Compile {include ...} tag
   *
   * @param string $tag_args
   * @return string
   */
  function _compile_include_php_tag($tag_args) {
    $attrs = $this
      ->_parse_attrs($tag_args);
    if (empty($attrs['file'])) {
      $this
        ->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
    }
    $assign_var = empty($attrs['assign']) ? '' : $this
      ->_dequote($attrs['assign']);
    $once_var = empty($attrs['once']) || $attrs['once'] == 'false' ? 'false' : 'true';
    $arg_list = array();
    foreach ($attrs as $arg_name => $arg_value) {
      if ($arg_name != 'file' and $arg_name != 'once' and $arg_name != 'assign') {
        if (is_bool($arg_value)) {
          $arg_value = $arg_value ? 'true' : 'false';
        }
        $arg_list[] = "'{$arg_name}' => {$arg_value}";
      }
    }
    $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '{$assign_var}', 'smarty_once' => {$once_var}, 'smarty_include_vars' => array(" . implode(',', $arg_list) . "))";
    return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php({$_params}, \$this); ?>" . $this->_additional_newline;
  }

  /**
   * Compile {section ...} tag
   *
   * @param string $tag_args
   * @return string
   */
  function _compile_section_start($tag_args) {
    $attrs = $this
      ->_parse_attrs($tag_args);
    $arg_list = array();
    $output = '<?php ';
    $section_name = $attrs['name'];
    if (empty($section_name)) {
      $this
        ->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
    }
    $output .= "unset(\$this->_sections[{$section_name}]);\n";
    $section_props = "\$this->_sections[{$section_name}]";
    foreach ($attrs as $attr_name => $attr_value) {
      switch ($attr_name) {
        case 'loop':
          $output .= "{$section_props}['loop'] = is_array(\$_loop={$attr_value}) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
          break;
        case 'show':
          if (is_bool($attr_value)) {
            $show_attr_value = $attr_value ? 'true' : 'false';
          }
          else {
            $show_attr_value = "(bool){$attr_value}";
          }
          $output .= "{$section_props}['show'] = {$show_attr_value};\n";
          break;
        case 'name':
          $output .= "{$section_props}['{$attr_name}'] = {$attr_value};\n";
          break;
        case 'max':
        case 'start':
          $output .= "{$section_props}['{$attr_name}'] = (int){$attr_value};\n";
          break;
        case 'step':
          $output .= "{$section_props}['{$attr_name}'] = ((int){$attr_value}) == 0 ? 1 : (int){$attr_value};\n";
          break;
        default:
          $this
            ->_syntax_error("unknown section attribute - '{$attr_name}'", E_USER_ERROR, __FILE__, __LINE__);
          break;
      }
    }
    if (!isset($attrs['show'])) {
      $output .= "{$section_props}['show'] = true;\n";
    }
    if (!isset($attrs['loop'])) {
      $output .= "{$section_props}['loop'] = 1;\n";
    }
    if (!isset($attrs['max'])) {
      $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
    }
    else {
      $output .= "if ({$section_props}['max'] < 0)\n" . "    {$section_props}['max'] = {$section_props}['loop'];\n";
    }
    if (!isset($attrs['step'])) {
      $output .= "{$section_props}['step'] = 1;\n";
    }
    if (!isset($attrs['start'])) {
      $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
    }
    else {
      $output .= "if ({$section_props}['start'] < 0)\n" . "    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" . "else\n" . "    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
    }
    $output .= "if ({$section_props}['show']) {\n";
    if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
      $output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
    }
    else {
      $output .= "    {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
    }
    $output .= "    if ({$section_props}['total'] == 0)\n" . "        {$section_props}['show'] = false;\n" . "} else\n" . "    {$section_props}['total'] = 0;\n";
    $output .= "if ({$section_props}['show']):\n";
    $output .= "\n            for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;\n                 {$section_props}['iteration'] <= {$section_props}['total'];\n                 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
    $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
    $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
    $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
    $output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
    $output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
    $output .= "?>";
    return $output;
  }

  /**
   * Compile {foreach ...} tag.
   *
   * @param string $tag_args
   * @return string
   */
  function _compile_foreach_start($tag_args) {
    $attrs = $this
      ->_parse_attrs($tag_args);
    $arg_list = array();
    if (empty($attrs['from'])) {
      return $this
        ->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
    }
    $from = $attrs['from'];
    if (empty($attrs['item'])) {
      return $this
        ->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
    }
    $item = $this
      ->_dequote($attrs['item']);
    if (!preg_match('~^\\w+$~', $item)) {
      return $this
        ->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
    }
    if (isset($attrs['key'])) {
      $key = $this
        ->_dequote($attrs['key']);
      if (!preg_match('~^\\w+$~', $key)) {
        return $this
          ->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
      }
      $key_part = "\$this->_tpl_vars['{$key}'] => ";
    }
    else {
      $key = null;
      $key_part = '';
    }
    if (isset($attrs['name'])) {
      $name = $attrs['name'];
    }
    else {
      $name = null;
    }
    $output = '<?php ';
    $output .= "\$_from = {$from}; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
    if (isset($name)) {
      $foreach_props = "\$this->_foreach[{$name}]";
      $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
      $output .= "if ({$foreach_props}['total'] > 0):\n";
      $output .= "    foreach (\$_from as {$key_part}\$this->_tpl_vars['{$item}']):\n";
      $output .= "        {$foreach_props}['iteration']++;\n";
    }
    else {
      $output .= "if (count(\$_from)):\n";
      $output .= "    foreach (\$_from as {$key_part}\$this->_tpl_vars['{$item}']):\n";
    }
    $output .= '?>';
    return $output;
  }

  /**
   * Compile {capture} .. {/capture} tags
   *
   * @param boolean $start true if this is the {capture} tag
   * @param string $tag_args
   * @return string
   */
  function _compile_capture_tag($start, $tag_args = '') {
    $attrs = $this
      ->_parse_attrs($tag_args);
    if ($start) {
      if (isset($attrs['name'])) {
        $buffer = $attrs['name'];
      }
      else {
        $buffer = "'default'";
      }
      if (isset($attrs['assign'])) {
        $assign = $attrs['assign'];
      }
      else {
        $assign = null;
      }
      $output = "<?php ob_start(); ?>";
      $this->_capture_stack[] = array(
        $buffer,
        $assign,
      );
    }
    else {
      list($buffer, $assign) = array_pop($this->_capture_stack);
      $output = "<?php \$this->_smarty_vars['capture'][{$buffer}] = ob_get_contents(); ";
      if (isset($assign)) {
        $output .= " \$this->assign({$assign}, ob_get_contents());";
      }
      $output .= "ob_end_clean(); ?>";
    }
    return $output;
  }

  /**
   * Compile {if ...} tag
   *
   * @param string $tag_args
   * @param boolean $elseif if true, uses elseif instead of if
   * @return string
   */
  function _compile_if_tag($tag_args, $elseif = false) {

    /* Tokenize args for 'if' tag. */
    preg_match_all('~(?>
                ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
                ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
                \\-?0[xX][0-9a-fA-F]+|\\-?\\d+(?:\\.\\d+)?|\\.\\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\\&\\&|\\|\\||\\(|\\)|,|\\!|\\^|=|\\&|\\~|<|>|\\||\\%|\\+|\\-|\\/|\\*|\\@    | # valid non-word token
                \\b\\w+\\b                                                        | # valid word token
                \\S+                                                           # anything else
                )~x', $tag_args, $match);
    $tokens = $match[0];

    // make sure we have balanced parenthesis
    $token_count = array_count_values($tokens);
    if (isset($token_count['(']) && $token_count['('] != $token_count[')']) {
      $this
        ->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
    }
    $is_arg_stack = array();
    for ($i = 0; $i < count($tokens); $i++) {
      $token =& $tokens[$i];
      switch (strtolower($token)) {
        case '!':
        case '%':
        case '!==':
        case '==':
        case '===':
        case '>':
        case '<':
        case '!=':
        case '<>':
        case '<<':
        case '>>':
        case '<=':
        case '>=':
        case '&&':
        case '||':
        case '|':
        case '^':
        case '&':
        case '~':
        case ')':
        case ',':
        case '+':
        case '-':
        case '*':
        case '/':
        case '@':
          break;
        case 'eq':
          $token = '==';
          break;
        case 'ne':
        case 'neq':
          $token = '!=';
          break;
        case 'lt':
          $token = '<';
          break;
        case 'le':
        case 'lte':
          $token = '<=';
          break;
        case 'gt':
          $token = '>';
          break;
        case 'ge':
        case 'gte':
          $token = '>=';
          break;
        case 'and':
          $token = '&&';
          break;
        case 'or':
          $token = '||';
          break;
        case 'not':
          $token = '!';
          break;
        case 'mod':
          $token = '%';
          break;
        case '(':
          array_push($is_arg_stack, $i);
          break;
        case 'is':

          /* If last token was a ')', we operate on the parenthesized
             expression. The start of the expression is on the stack.
             Otherwise, we operate on the last encountered token. */
          if ($tokens[$i - 1] == ')') {
            $is_arg_start = array_pop($is_arg_stack);
          }
          else {
            $is_arg_start = $i - 1;
          }

          /* Construct the argument for 'is' expression, so it knows
             what to operate on. */
          $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));

          /* Pass all tokens from next one until the end to the
             'is' expression parsing function. The function will
             return modified tokens, where the first one is the result
             of the 'is' expression and the rest are the tokens it
             didn't touch. */
          $new_tokens = $this
            ->_parse_is_expr($is_arg, array_slice($tokens, $i + 1));

          /* Replace the old tokens with the new ones. */
          array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);

          /* Adjust argument start so that it won't change from the
             current position for the next iteration. */
          $i = $is_arg_start;
          break;
        default:
          if (preg_match('~^' . $this->_func_regexp . '$~', $token)) {

            // function call
            if ($this->security && !in_array($token, $this->security_settings['IF_FUNCS'])) {
              $this
                ->_syntax_error("(secure mode) '{$token}' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
            }
          }
          elseif (preg_match('~^' . $this->_var_regexp . '$~', $token) && isset($tokens[$i + 1]) && $tokens[$i + 1] == '(') {

            // variable function call
            $this
              ->_syntax_error("variable function call '{$token}' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
          }
          elseif (preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {

            // object or variable
            $token = $this
              ->_parse_var_props($token);
          }
          elseif (is_numeric($token)) {

            // number, skip it
          }
          else {
            $this
              ->_syntax_error("unidentified token '{$token}'", E_USER_ERROR, __FILE__, __LINE__);
          }
          break;
      }
    }
    if ($elseif) {
      return '<?php elseif (' . implode(' ', $tokens) . '): ?>';
    }
    else {
      return '<?php if (' . implode(' ', $tokens) . '): ?>';
    }
  }
  function _compile_arg_list($type, $name, $attrs, &$cache_code) {
    $arg_list = array();
    if (isset($type) && isset($name) && isset($this->_plugins[$type]) && isset($this->_plugins[$type][$name]) && empty($this->_plugins[$type][$name][4]) && is_array($this->_plugins[$type][$name][5])) {

      /* we have a list of parameters that should be cached */
      $_cache_attrs = $this->_plugins[$type][$name][5];
      $_count = $this->_cache_attrs_count++;
      $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('{$this->_cache_serial}','{$_count}');";
    }
    else {

      /* no parameters are cached */
      $_cache_attrs = null;
    }
    foreach ($attrs as $arg_name => $arg_value) {
      if (is_bool($arg_value)) {
        $arg_value = $arg_value ? 'true' : 'false';
      }
      if (is_null($arg_value)) {
        $arg_value = 'null';
      }
      if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
        $arg_list[] = "'{$arg_name}' => (\$this->_cache_including) ? \$_cache_attrs['{$arg_name}'] : (\$_cache_attrs['{$arg_name}']={$arg_value})";
      }
      else {
        $arg_list[] = "'{$arg_name}' => {$arg_value}";
      }
    }
    return $arg_list;
  }

  /**
   * Parse is expression
   *
   * @param string $is_arg
   * @param array $tokens
   * @return array
   */
  function _parse_is_expr($is_arg, $tokens) {
    $expr_end = 0;
    $negate_expr = false;
    if (($first_token = array_shift($tokens)) == 'not') {
      $negate_expr = true;
      $expr_type = array_shift($tokens);
    }
    else {
      $expr_type = $first_token;
    }
    switch ($expr_type) {
      case 'even':
        if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
          $expr_end++;
          $expr_arg = $tokens[$expr_end++];
          $expr = "!(1 & ({$is_arg} / " . $this
            ->_parse_var_props($expr_arg) . "))";
        }
        else {
          $expr = "!(1 & {$is_arg})";
        }
        break;
      case 'odd':
        if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
          $expr_end++;
          $expr_arg = $tokens[$expr_end++];
          $expr = "(1 & ({$is_arg} / " . $this
            ->_parse_var_props($expr_arg) . "))";
        }
        else {
          $expr = "(1 & {$is_arg})";
        }
        break;
      case 'div':
        if (@$tokens[$expr_end] == 'by') {
          $expr_end++;
          $expr_arg = $tokens[$expr_end++];
          $expr = "!({$is_arg} % " . $this
            ->_parse_var_props($expr_arg) . ")";
        }
        else {
          $this
            ->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
        }
        break;
      default:
        $this
          ->_syntax_error("unknown 'is' expression - '{$expr_type}'", E_USER_ERROR, __FILE__, __LINE__);
        break;
    }
    if ($negate_expr) {
      $expr = "!({$expr})";
    }
    array_splice($tokens, 0, $expr_end, $expr);
    return $tokens;
  }

  /**
   * Parse attribute string
   *
   * @param string $tag_args
   * @return array
   */
  function _parse_attrs($tag_args) {

    /* Tokenize tag attributes. */
    preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\\s]+)
                         )+ |
                         [=]
                        ~x', $tag_args, $match);
    $tokens = $match[0];
    $attrs = array();

    /* Parse state:
       0 - expecting attribute name
       1 - expecting '='
       2 - expecting attribute value (not '=') */
    $state = 0;
    foreach ($tokens as $token) {
      switch ($state) {
        case 0:

          /* If the token is a valid identifier, we set attribute name
             and go to state 1. */
          if (preg_match('~^\\w+$~', $token)) {
            $attr_name = $token;
            $state = 1;
          }
          else {
            $this
              ->_syntax_error("invalid attribute name: '{$token}'", E_USER_ERROR, __FILE__, __LINE__);
          }
          break;
        case 1:

          /* If the token is '=', then we go to state 2. */
          if ($token == '=') {
            $state = 2;
          }
          else {
            $this
              ->_syntax_error("expecting '=' after attribute name '{$last_token}'", E_USER_ERROR, __FILE__, __LINE__);
          }
          break;
        case 2:

          /* If token is not '=', we set the attribute value and go to
             state 0. */
          if ($token != '=') {

            /* We booleanize the token if it's a non-quoted possible
               boolean value. */
            if (preg_match('~^(on|yes|true)$~', $token)) {
              $token = 'true';
            }
            else {
              if (preg_match('~^(off|no|false)$~', $token)) {
                $token = 'false';
              }
              else {
                if ($token == 'null') {
                  $token = 'null';
                }
                else {
                  if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {

                    /* treat integer literally */
                  }
                  else {
                    if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {

                      /* treat as a string, double-quote it escaping quotes */
                      $token = '"' . addslashes($token) . '"';
                    }
                  }
                }
              }
            }
            $attrs[$attr_name] = $token;
            $state = 0;
          }
          else {
            $this
              ->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
          }
          break;
      }
      $last_token = $token;
    }
    if ($state != 0) {
      if ($state == 1) {
        $this
          ->_syntax_error("expecting '=' after attribute name '{$last_token}'", E_USER_ERROR, __FILE__, __LINE__);
      }
      else {
        $this
          ->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
      }
    }
    $this
      ->_parse_vars_props($attrs);
    return $attrs;
  }

  /**
   * compile multiple variables and section properties tokens into
   * PHP code
   *
   * @param array $tokens
   */
  function _parse_vars_props(&$tokens) {
    foreach ($tokens as $key => $val) {
      $tokens[$key] = $this
        ->_parse_var_props($val);
    }
  }

  /**
   * compile single variable and section properties token into
   * PHP code
   *
   * @param string $val
   * @param string $tag_attrs
   * @return string
   */
  function _parse_var_props($val) {
    $val = trim($val);
    if (preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {

      // $ variable or object
      $return = $this
        ->_parse_var($match[1]);
      $modifiers = $match[2];
      if (!empty($this->default_modifiers) && !preg_match('~(^|\\|)smarty:nodefaults($|\\|)~', $modifiers)) {
        $_default_mod_string = implode('|', (array) $this->default_modifiers);
        $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
      }
      $this
        ->_parse_modifiers($return, $modifiers);
      return $return;
    }
    elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {

      // double quoted text
      preg_match('~^(' . $this->_db_qstr_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match);
      $return = $this
        ->_expand_quoted_text($match[1]);
      if ($match[2] != '') {
        $this
          ->_parse_modifiers($return, $match[2]);
      }
      return $return;
    }
    elseif (preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {

      // numerical constant
      preg_match('~^(' . $this->_num_const_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match);
      if ($match[2] != '') {
        $this
          ->_parse_modifiers($match[1], $match[2]);
        return $match[1];
      }
    }
    elseif (preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {

      // single quoted text
      preg_match('~^(' . $this->_si_qstr_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match);
      if ($match[2] != '') {
        $this
          ->_parse_modifiers($match[1], $match[2]);
        return $match[1];
      }
    }
    elseif (preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {

      // config var
      return $this
        ->_parse_conf_var($val);
    }
    elseif (preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {

      // section var
      return $this
        ->_parse_section_prop($val);
    }
    elseif (!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {

      // literal string
      return $this
        ->_expand_quoted_text('"' . $val . '"');
    }
    return $val;
  }

  /**
   * expand quoted text with embedded variables
   *
   * @param string $var_expr
   * @return string
   */
  function _expand_quoted_text($var_expr) {

    // if contains unescaped $, expand it
    if (preg_match_all('~(?:\\`(?<!\\\\)\\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\\`)|(?:(?<!\\\\)\\$\\w+(\\[[a-zA-Z0-9]+\\])*)~', $var_expr, $_match)) {
      $_match = $_match[0];
      rsort($_match);
      reset($_match);
      foreach ($_match as $_var) {
        $var_expr = str_replace($_var, '".(' . $this
          ->_parse_var(str_replace('`', '', $_var)) . ')."', $var_expr);
      }
      $_return = preg_replace('~\\.""|(?<!\\\\)""\\.~', '', $var_expr);
    }
    else {
      $_return = $var_expr;
    }

    // replace double quoted literal string with single quotes
    $_return = preg_replace('~^"([\\s\\w]+)"$~', "'\\1'", $_return);
    return $_return;
  }

  /**
   * parse variable expression into PHP code
   *
   * @param string $var_expr
   * @param string $output
   * @return string
   */
  function _parse_var($var_expr) {
    $_has_math = false;
    $_math_vars = preg_split('~(' . $this->_dvar_math_regexp . '|' . $this->_qstr_regexp . ')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
    if (count($_math_vars) > 1) {
      $_first_var = "";
      $_complete_var = "";
      $_output = "";

      // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
      foreach ($_math_vars as $_k => $_math_var) {
        $_math_var = $_math_vars[$_k];
        if (!empty($_math_var) || is_numeric($_math_var)) {

          // hit a math operator, so process the stuff which came before it
          if (preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
            $_has_math = true;
            if (!empty($_complete_var) || is_numeric($_complete_var)) {
              $_output .= $this
                ->_parse_var($_complete_var);
            }

            // just output the math operator to php
            $_output .= $_math_var;
            if (empty($_first_var)) {
              $_first_var = $_complete_var;
            }
            $_complete_var = "";
          }
          else {
            $_complete_var .= $_math_var;
          }
        }
      }
      if ($_has_math) {
        if (!empty($_complete_var) || is_numeric($_complete_var)) {
          $_output .= $this
            ->_parse_var($_complete_var);
        }

        // get the modifiers working (only the last var from math + modifier is left)
        $var_expr = $_complete_var;
      }
    }

    // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
    if (is_numeric($var_expr[0])) {
      $_var_ref = $var_expr;
    }
    else {
      $_var_ref = substr($var_expr, 1);
    }
    if (!$_has_math) {

      // get [foo] and .foo and ->foo and (...) pieces
      preg_match_all('~(?:^\\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\\$?\\w+|\\.\\$?\\w+|\\S+~', $_var_ref, $match);
      $_indexes = $match[0];
      $_var_name = array_shift($_indexes);

      /* Handle $smarty.* variable references as a special case. */
      if ($_var_name == 'smarty') {

        /*
         * If the reference could be compiled, use the compiled output;
         * otherwise, fall back on the $smarty variable generated at
         * run-time.
         */
        if (($smarty_ref = $this
          ->_compile_smarty_ref($_indexes)) !== null) {
          $_output = $smarty_ref;
        }
        else {
          $_var_name = substr(array_shift($_indexes), 1);
          $_output = "\$this->_smarty_vars['{$_var_name}']";
        }
      }
      elseif (is_numeric($_var_name) && is_numeric($var_expr[0])) {

        // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
        if (count($_indexes) > 0) {
          $_var_name .= implode("", $_indexes);
          $_indexes = array();
        }
        $_output = $_var_name;
      }
      else {
        $_output = "\$this->_tpl_vars['{$_var_name}']";
      }
      foreach ($_indexes as $_index) {
        if ($_index[0] == '[') {
          $_index = substr($_index, 1, -1);
          if (is_numeric($_index)) {
            $_output .= "[{$_index}]";
          }
          elseif ($_index[0] == '$') {
            if (strpos($_index, '.') !== false) {
              $_output .= '[' . $this
                ->_parse_var($_index) . ']';
            }
            else {
              $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
            }
          }
          else {
            $_var_parts = explode('.', $_index);
            $_var_section = $_var_parts[0];
            $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
            $_output .= "[\$this->_sections['{$_var_section}']['{$_var_section_prop}']]";
          }
        }
        else {
          if ($_index[0] == '.') {
            if ($_index[1] == '$') {
              $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
            }
            else {
              $_output .= "['" . substr($_index, 1) . "']";
            }
          }
          else {
            if (substr($_index, 0, 2) == '->') {
              if (substr($_index, 2, 2) == '__') {
                $this
                  ->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
              }
              elseif ($this->security && substr($_index, 2, 1) == '_') {
                $this
                  ->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
              }
              elseif ($_index[2] == '$') {
                if ($this->security) {
                  $this
                    ->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
                }
                else {
                  $_output .= '->{(($_var=$this->_tpl_vars[\'' . substr($_index, 3) . '\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
                }
              }
              else {
                $_output .= $_index;
              }
            }
            elseif ($_index[0] == '(') {
              $_index = $this
                ->_parse_parenth_args($_index);
              $_output .= $_index;
            }
            else {
              $_output .= $_index;
            }
          }
        }
      }
    }
    return $_output;
  }

  /**
   * parse arguments in function call parenthesis
   *
   * @param string $parenth_args
   * @return string
   */
  function _parse_parenth_args($parenth_args) {
    preg_match_all('~' . $this->_param_regexp . '~', $parenth_args, $match);
    $orig_vals = $match = $match[0];
    $this
      ->_parse_vars_props($match);
    $replace = array();
    for ($i = 0, $count = count($match); $i < $count; $i++) {
      $replace[$orig_vals[$i]] = $match[$i];
    }
    return strtr($parenth_args, $replace);
  }

  /**
   * parse configuration variable expression into PHP code
   *
   * @param string $conf_var_expr
   */
  function _parse_conf_var($conf_var_expr) {
    $parts = explode('|', $conf_var_expr, 2);
    $var_ref = $parts[0];
    $modifiers = isset($parts[1]) ? $parts[1] : '';
    $var_name = substr($var_ref, 1, -1);
    $output = "\$this->_config[0]['vars']['{$var_name}']";
    $this
      ->_parse_modifiers($output, $modifiers);
    return $output;
  }

  /**
   * parse section property expression into PHP code
   *
   * @param string $section_prop_expr
   * @return string
   */
  function _parse_section_prop($section_prop_expr) {
    $parts = explode('|', $section_prop_expr, 2);
    $var_ref = $parts[0];
    $modifiers = isset($parts[1]) ? $parts[1] : '';
    preg_match('!%(\\w+)\\.(\\w+)%!', $var_ref, $match);
    $section_name = $match[1];
    $prop_name = $match[2];
    $output = "\$this->_sections['{$section_name}']['{$prop_name}']";
    $this
      ->_parse_modifiers($output, $modifiers);
    return $output;
  }

  /**
   * parse modifier chain into PHP code
   *
   * sets $output to parsed modified chain
   * @param string $output
   * @param string $modifier_string
   */
  function _parse_modifiers(&$output, $modifier_string) {
    preg_match_all('~\\|(@?\\w+)((?>:(?:' . $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
    list(, $_modifiers, $modifier_arg_strings) = $_match;
    for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
      $_modifier_name = $_modifiers[$_i];
      if ($_modifier_name == 'smarty') {

        // skip smarty modifier
        continue;
      }
      preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
      $_modifier_args = $_match[1];
      if ($_modifier_name[0] == '@') {
        $_map_array = false;
        $_modifier_name = substr($_modifier_name, 1);
      }
      else {
        $_map_array = true;
      }
      if (empty($this->_plugins['modifier'][$_modifier_name]) && !$this
        ->_get_plugin_filepath('modifier', $_modifier_name) && function_exists($_modifier_name)) {
        if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
          $this
            ->_trigger_fatal_error("[plugin] (secure mode) modifier '{$_modifier_name}' is not allowed", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
        }
        else {
          $this->_plugins['modifier'][$_modifier_name] = array(
            $_modifier_name,
            null,
            null,
            false,
          );
        }
      }
      $this
        ->_add_plugin('modifier', $_modifier_name);
      $this
        ->_parse_vars_props($_modifier_args);
      if ($_modifier_name == 'default') {

        // supress notifications of default modifier vars and args
        if ($output[0] == '$') {
          $output = '@' . $output;
        }
        if (isset($_modifier_args[0]) && $_modifier_args[0][0] == '$') {
          $_modifier_args[0] = '@' . $_modifier_args[0];
        }
      }
      if (count($_modifier_args) > 0) {
        $_modifier_args = ', ' . implode(', ', $_modifier_args);
      }
      else {
        $_modifier_args = '';
      }
      if ($_map_array) {
        $output = "((is_array(\$_tmp={$output})) ? \$this->_run_mod_handler('{$_modifier_name}', true, \$_tmp{$_modifier_args}) : " . $this
          ->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp{$_modifier_args}))";
      }
      else {
        $output = $this
          ->_compile_plugin_call('modifier', $_modifier_name) . "({$output}{$_modifier_args})";
      }
    }
  }

  /**
   * add plugin
   *
   * @param string $type
   * @param string $name
   * @param boolean? $delayed_loading
   */
  function _add_plugin($type, $name, $delayed_loading = null) {
    if (!isset($this->_plugin_info[$type])) {
      $this->_plugin_info[$type] = array();
    }
    if (!isset($this->_plugin_info[$type][$name])) {
      $this->_plugin_info[$type][$name] = array(
        $this->_current_file,
        $this->_current_line_no,
        $delayed_loading,
      );
    }
  }

  /**
   * Compiles references of type $smarty.foo
   *
   * @param string $indexes
   * @return string
   */
  function _compile_smarty_ref(&$indexes) {

    /* Extract the reference name. */
    $_ref = substr($indexes[0], 1);
    foreach ($indexes as $_index_no => $_index) {
      if ($_index[0] != '.' && $_index_no < 2 || !preg_match('~^(\\.|\\[|->)~', $_index)) {
        $this
          ->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
      }
    }
    switch ($_ref) {
      case 'now':
        $compiled_ref = 'time()';
        $_max_index = 1;
        break;
      case 'foreach':
        array_shift($indexes);
        $_var = $this
          ->_parse_var_props(substr($indexes[0], 1));
        $_propname = substr($indexes[1], 1);
        $_max_index = 1;
        switch ($_propname) {
          case 'index':
            array_shift($indexes);
            $compiled_ref = "(\$this->_foreach[{$_var}]['iteration']-1)";
            break;
          case 'first':
            array_shift($indexes);
            $compiled_ref = "(\$this->_foreach[{$_var}]['iteration'] <= 1)";
            break;
          case 'last':
            array_shift($indexes);
            $compiled_ref = "(\$this->_foreach[{$_var}]['iteration'] == \$this->_foreach[{$_var}]['total'])";
            break;
          case 'show':
            array_shift($indexes);
            $compiled_ref = "(\$this->_foreach[{$_var}]['total'] > 0)";
            break;
          default:
            unset($_max_index);
            $compiled_ref = "\$this->_foreach[{$_var}]";
        }
        break;
      case 'section':
        array_shift($indexes);
        $_var = $this
          ->_parse_var_props(substr($indexes[0], 1));
        $compiled_ref = "\$this->_sections[{$_var}]";
        break;
      case 'get':
        $compiled_ref = $this->request_use_auto_globals ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
        break;
      case 'post':
        $compiled_ref = $this->request_use_auto_globals ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
        break;
      case 'cookies':
        $compiled_ref = $this->request_use_auto_globals ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
        break;
      case 'env':
        $compiled_ref = $this->request_use_auto_globals ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
        break;
      case 'server':
        $compiled_ref = $this->request_use_auto_globals ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
        break;
      case 'session':
        $compiled_ref = $this->request_use_auto_globals ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
        break;

      /*
       * These cases are handled either at run-time or elsewhere in the
       * compiler.
       */
      case 'request':
        if ($this->request_use_auto_globals) {
          $compiled_ref = '$_REQUEST';
          break;
        }
        else {
          $this->_init_smarty_vars = true;
        }
        return null;
      case 'capture':
        return null;
      case 'template':
        $compiled_ref = "'{$this->_current_file}'";
        $_max_index = 1;
        break;
      case 'version':
        $compiled_ref = "'{$this->_version}'";
        $_max_index = 1;
        break;
      case 'const':
        if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
          $this
            ->_syntax_error("(secure mode) constants not permitted", E_USER_WARNING, __FILE__, __LINE__);
          return;
        }
        array_shift($indexes);
        if (preg_match('!^\\.\\w+$!', $indexes[0])) {
          $compiled_ref = '@' . substr($indexes[0], 1);
        }
        else {
          $_val = $this
            ->_parse_var_props(substr($indexes[0], 1));
          $compiled_ref = '@constant(' . $_val . ')';
        }
        $_max_index = 1;
        break;
      case 'config':
        $compiled_ref = "\$this->_config[0]['vars']";
        $_max_index = 3;
        break;
      case 'ldelim':
        $compiled_ref = "'{$this->left_delimiter}'";
        break;
      case 'rdelim':
        $compiled_ref = "'{$this->right_delimiter}'";
        break;
      default:
        $this
          ->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
        break;
    }
    if (isset($_max_index) && count($indexes) > $_max_index) {
      $this
        ->_syntax_error('$smarty' . implode('', $indexes) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
    }
    array_shift($indexes);
    return $compiled_ref;
  }

  /**
   * compiles call to plugin of type $type with name $name
   * returns a string containing the function-name or method call
   * without the paramter-list that would have follow to make the
   * call valid php-syntax
   *
   * @param string $type
   * @param string $name
   * @return string
   */
  function _compile_plugin_call($type, $name) {
    if (isset($this->_plugins[$type][$name])) {

      /* plugin loaded */
      if (is_array($this->_plugins[$type][$name][0])) {
        return (is_object($this->_plugins[$type][$name][0][0]) ? "\$this->_plugins['{$type}']['{$name}'][0][0]->" : (string) $this->_plugins[$type][$name][0][0] . '::') . $this->_plugins[$type][$name][0][1];
      }
      else {

        /* function callback */
        return $this->_plugins[$type][$name][0];
      }
    }
    else {

      /* plugin not loaded -> auto-loadable-plugin */
      return 'smarty_' . $type . '_' . $name;
    }
  }

  /**
   * load pre- and post-filters
   */
  function _load_filters() {
    if (count($this->_plugins['prefilter']) > 0) {
      foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
        if ($prefilter === false) {
          unset($this->_plugins['prefilter'][$filter_name]);
          $_params = array(
            'plugins' => array(
              array(
                'prefilter',
                $filter_name,
                null,
                null,
                false,
              ),
            ),
          );
          require_once SMARTY_CORE_DIR . 'core.load_plugins.php';
          smarty_core_load_plugins($_params, $this);
        }
      }
    }
    if (count($this->_plugins['postfilter']) > 0) {
      foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
        if ($postfilter === false) {
          unset($this->_plugins['postfilter'][$filter_name]);
          $_params = array(
            'plugins' => array(
              array(
                'postfilter',
                $filter_name,
                null,
                null,
                false,
              ),
            ),
          );
          require_once SMARTY_CORE_DIR . 'core.load_plugins.php';
          smarty_core_load_plugins($_params, $this);
        }
      }
    }
  }

  /**
   * Quote subpattern references
   *
   * @param string $string
   * @return string
   */
  function _quote_replace($string) {
    return strtr($string, array(
      '\\' => '\\\\',
      '$' => '\\$',
    ));
  }

  /**
   * display Smarty syntax error
   *
   * @param string $error_msg
   * @param integer $error_type
   * @param string $file
   * @param integer $line
   */
  function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file = null, $line = null) {
    $this
      ->_trigger_fatal_error("syntax error: {$error_msg}", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
  }

  /**
   * check if the compilation changes from cacheable to
   * non-cacheable state with the beginning of the current
   * plugin. return php-code to reflect the transition.
   * @return string
   */
  function _push_cacheable_state($type, $name) {
    $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
    if ($_cacheable || 0 < $this->_cacheable_state++) {
      return '';
    }
    if (!isset($this->_cache_serial)) {
      $this->_cache_serial = md5(uniqid('Smarty'));
    }
    $_ret = 'if ($this->caching && !$this->_cache_including) { echo \'{nocache:' . $this->_cache_serial . '#' . $this->_nocache_count . '}\';}';
    return $_ret;
  }

  /**
   * check if the compilation changes from non-cacheable to
   * cacheable state with the end of the current plugin return
   * php-code to reflect the transition.
   * @return string
   */
  function _pop_cacheable_state($type, $name) {
    $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
    if ($_cacheable || --$this->_cacheable_state > 0) {
      return '';
    }
    return 'if ($this->caching && !$this->_cache_including) { echo \'{/nocache:' . $this->_cache_serial . '#' . $this->_nocache_count++ . '}\';}';
  }

  /**
   * push opening tag-name, file-name and line-number on the tag-stack
   * @param string the opening tag's name
   */
  function _push_tag($open_tag) {
    array_push($this->_tag_stack, array(
      $open_tag,
      $this->_current_line_no,
    ));
  }

  /**
   * pop closing tag-name
   * raise an error if this stack-top doesn't match with the closing tag
   * @param string the closing tag's name
   * @return string the opening tag's name
   */
  function _pop_tag($close_tag) {
    $message = '';
    if (count($this->_tag_stack) > 0) {
      list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
      if ($close_tag == $_open_tag) {
        return $_open_tag;
      }
      if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif')) {
        return $this
          ->_pop_tag($close_tag);
      }
      if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
        $this
          ->_pop_tag($close_tag);
        return $_open_tag;
      }
      if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
        $this
          ->_pop_tag($close_tag);
        return $_open_tag;
      }
      if ($_open_tag == 'else' || $_open_tag == 'elseif') {
        $_open_tag = 'if';
      }
      elseif ($_open_tag == 'sectionelse') {
        $_open_tag = 'section';
      }
      elseif ($_open_tag == 'foreachelse') {
        $_open_tag = 'foreach';
      }
      $message = " expected {/{$_open_tag}} (opened line {$_line_no}).";
    }
    $this
      ->_syntax_error("mismatched tag {/{$close_tag}}.{$message}", E_USER_ERROR, __FILE__, __LINE__);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Smarty::$autoload_filters property This indicates which filters are automatically loaded into Smarty.
Smarty::$cache_dir property The name of the directory for cache files.
Smarty::$cache_handler_func property The function used for cache file handling. If not set, built-in caching is used.
Smarty::$cache_lifetime property This is the number of seconds cached content will persist. <ul> <li>0 = always regenerate cache</li> <li>-1 = never expires</li> </ul>
Smarty::$cache_modified_check property Only used when $caching is enabled. If true, then If-Modified-Since headers are respected with cached content, and appropriate HTTP headers are sent. This way repeated hits to a cached page do not send the entire page to the client every time.
Smarty::$caching property This enables template caching. <ul> <li>0 = no caching</li> <li>1 = use class cache_lifetime value</li> <li>2 = use cache_lifetime in cache file</li> </ul>
Smarty::$compiler_class property The class used for compiling templates.
Smarty::$compiler_file property The file that contains the compiler class. This can a full pathname, or relative to the php_include path.
Smarty::$compile_check property This tells Smarty whether to check for recompiling or not. Recompiling does not need to happen unless a template or config file is changed. Typically you enable this during development, and disable for production.
Smarty::$compile_dir property The directory where compiled templates are located.
Smarty::$compile_id property Set this if you want different sets of compiled files for the same templates. This is useful for things like different languages. Instead of creating separate sets of templates per language, you set different compile_ids like 'en' and…
Smarty::$config_booleanize property This tells whether or not to automatically booleanize config file variables. If enabled, then the strings "on", "true", and "yes" are treated as boolean true, and "off", "false" and "no" are…
Smarty::$config_class property The class used to load config vars.
Smarty::$config_dir property The directory where config files are located.
Smarty::$config_fix_newlines property This tells whether or not automatically fix newlines in config files. It basically converts \r (mac) or \r\n (dos) to \n
Smarty::$config_overwrite property This tells if config file vars of the same name overwrite each other or not. if disabled, same name variables are accumulated in an array.
Smarty::$config_read_hidden property This tells whether hidden sections [.foobar] are readable from the tempalates or not. Normally you would never allow this since that is the point behind hidden sections: the application can access them, but the templates cannot.
Smarty::$debugging property If debugging is enabled, a debug console window will display when the page loads (make sure your browser allows unrequested popup windows)
Smarty::$debugging_ctrl property This determines if debugging is enable-able from the browser. <ul> <li>NONE => no debugging control allowed</li> <li>URL => enable debugging when SMARTY_DEBUG is found in the URL.</li> </ul> @link…
Smarty::$debug_tpl property This is the path to the debug console template. If not set, the default one will be used.
Smarty::$default_modifiers property This is a list of the modifiers to apply to all template variables. Put each modifier in a separate array element in the order you want them applied. example: <code>array('escape:"htmlall"');</code>
Smarty::$default_resource_type property This is the resource type to be used when not specified at the beginning of the resource path. examples: $smarty->display('file:index.tpl'); $smarty->display('db:index.tpl'); $smarty->display('index.tpl'); //…
Smarty::$default_template_handler_func property If a template cannot be found, this PHP function will be executed. Useful for creating templates on-the-fly or other special action.
Smarty::$error_reporting property When set, smarty does uses this value as error_reporting-level.
Smarty::$force_compile property This forces templates to compile every time. Useful for development or debugging.
Smarty::$left_delimiter property The left delimiter used for the template tags.
Smarty::$php_handling property This determines how Smarty handles "<?php ... ?>" tags in templates. possible values: <ul> <li>SMARTY_PHP_PASSTHRU -> print tags as plain text</li> <li>SMARTY_PHP_QUOTE -> escape tags as…
Smarty::$plugins_dir property An array of directories searched for plugins.
Smarty::$request_use_auto_globals property Indicates wether $HTTP_*_VARS[] (request_use_auto_globals=false) are uses as request-vars or $_*[]-vars. note: if request_use_auto_globals is true, then $request_vars_order has no effect, but the php-ini-value "gpc_order"
Smarty::$request_vars_order property The order in which request variables are registered, similar to variables_order in php.ini E = Environment, G = GET, P = POST, C = Cookies, S = Server
Smarty::$right_delimiter property The right delimiter used for the template tags.
Smarty::$secure_dir property This is the list of template directories that are considered secure. This is used only if {@link $security} is enabled. One directory per array element. {@link $template_dir} is in this list implicitly.
Smarty::$security property This enables template security. When enabled, many things are restricted in the templates that normally would go unchecked. This is useful when untrusted parties are editing templates and you want a reasonable level of security. (no direct execution…
Smarty::$security_settings property These are the security settings for Smarty. They are used only when {@link $security} is enabled.
Smarty::$template_dir property The name of the directory where templates are located.
Smarty::$trusted_dir property This is an array of directories where trusted php scripts reside. {@link $security} is disabled during their inclusion/execution.
Smarty::$use_sub_dirs property This tells Smarty whether or not to use sub dirs in the cache/ and templates_c/ directories. sub directories better organized, but may not work well with PHP safe mode enabled.
Smarty::$_cache_including property indicate if the current code is used in a compiled include
Smarty::$_cache_info property info that makes up a cache file
Smarty::$_cache_serials property cache serials
Smarty::$_compile_id property for different compiled templates
Smarty::$_config property loaded configuration settings
Smarty::$_conf_obj property configuration object
Smarty::$_dir_perms property default dir permissions
Smarty::$_file_perms property default file permissions
Smarty::$_foreach property keeps track of foreach blocks
Smarty::$_inclusion_depth property current template inclusion depth
Smarty::$_plugins property table keeping track of plugins
Smarty::$_reg_objects property registered objects
Smarty::$_sections property keeps track of sections
Smarty::$_smarty_debug_id property text in URL to enable debug mode
Smarty::$_smarty_debug_info property debugging information for debug console
Smarty::$_smarty_md5 property md5 checksum of the string 'Smarty'
Smarty::$_smarty_vars property stores run-time $smarty.* vars
Smarty::$_tag_stack property keeps track of tag hierarchy
Smarty::$_tpl_vars property where assigned template vars are kept
Smarty::$_version property Smarty version number
Smarty::append function appends values to template variables
Smarty::append_by_ref function appends values to template variables by reference
Smarty::assign function assigns values to template variables
Smarty::assign_by_ref function assigns values to template variables by reference
Smarty::clear_all_assign function clear all the assigned template variables.
Smarty::clear_all_cache function clear the entire contents of cache (all templates)
Smarty::clear_assign function clear the given assigned template variable.
Smarty::clear_cache function clear cached content for the given template and cache id
Smarty::clear_compiled_tpl function clears compiled version of specified template resource, or all compiled template files if one is not specified. This function is for advanced use only, not normally needed.
Smarty::clear_config function clear configuration values
Smarty::config_load function load configuration values
Smarty::display function executes & displays the template results
Smarty::fetch function executes & returns or displays the template results
Smarty::get_config_vars function Returns an array containing config variables
Smarty::get_registered_object function return a reference to a registered object
Smarty::get_template_vars function Returns an array containing template variables
Smarty::is_cached function test to see if valid cache exists for this template
Smarty::load_filter function load a filter of specified type and name
Smarty::register_block function Registers block function to be used in templates
Smarty::register_compiler_function function Registers compiler function
Smarty::register_function function Registers custom function to be used in templates
Smarty::register_modifier function Registers modifier to be used in templates
Smarty::register_object function Registers object to be used in templates
Smarty::register_outputfilter function Registers an output filter function to apply to a template output
Smarty::register_postfilter function Registers a postfilter function to apply to a compiled template after compilation
Smarty::register_prefilter function Registers a prefilter function to apply to a template before compiling
Smarty::register_resource function Registers a resource to fetch a template
Smarty::Smarty function The class constructor.
Smarty::template_exists function Checks whether requested template exists.
Smarty::trigger_error function trigger Smarty error
Smarty::unregister_block function Unregisters block function
Smarty::unregister_compiler_function function Unregisters compiler function
Smarty::unregister_function function Unregisters custom function
Smarty::unregister_modifier function Unregisters modifier
Smarty::unregister_object function Unregisters object
Smarty::unregister_outputfilter function Unregisters an outputfilter function
Smarty::unregister_postfilter function Unregisters a postfilter function
Smarty::unregister_prefilter function Unregisters a prefilter function
Smarty::unregister_resource function Unregisters a resource
Smarty::_compile_resource function compile the template
Smarty::_compile_source function compile the given source
Smarty::_dequote function Remove starting and ending quotes from the string
Smarty::_eval function wrapper for eval() retaining $this
Smarty::_fetch_resource_info function fetch the template info. Gets timestamp, and source if get_source is true
Smarty::_get_auto_filename function get a concrete filename for automagically created content
Smarty::_get_auto_id function returns an auto_id for auto-file-functions
Smarty::_get_compile_path function Get the compile path for this resource
Smarty::_get_plugin_filepath function get filepath of requested plugin
Smarty::_include function wrapper for include() retaining $this
Smarty::_is_compiled function test if resource needs compiling
Smarty::_parse_resource_name function parse out the type and name from the resource
Smarty::_process_compiled_include_callback function callback function for preg_replace, to call a non-cacheable block
Smarty::_read_file function read in a file
Smarty::_run_mod_handler function Handle modifiers
Smarty::_smarty_cache_attrs function get or set an array of cached attributes for function that is not cacheable
Smarty::_smarty_include function
Smarty::_trigger_fatal_error function trigger Smarty plugin error
Smarty::_unlink function unlink a file, possibly using expiration time
Smarty_Compiler::$_additional_newline property
Smarty_Compiler::$_avar_regexp property
Smarty_Compiler::$_cacheable_state property
Smarty_Compiler::$_cache_attrs_count property
Smarty_Compiler::$_cache_include property name of optional cache include file Overrides Smarty::$_cache_include
Smarty_Compiler::$_cache_serial property
Smarty_Compiler::$_capture_stack property
Smarty_Compiler::$_current_file property
Smarty_Compiler::$_current_line_no property
Smarty_Compiler::$_cvar_regexp property
Smarty_Compiler::$_db_qstr_regexp property
Smarty_Compiler::$_dvar_guts_regexp property
Smarty_Compiler::$_dvar_regexp property
Smarty_Compiler::$_folded_blocks property
Smarty_Compiler::$_func_call_regexp property
Smarty_Compiler::$_func_regexp property
Smarty_Compiler::$_init_smarty_vars property
Smarty_Compiler::$_mod_regexp property
Smarty_Compiler::$_nocache_count property
Smarty_Compiler::$_num_const_regexp property
Smarty_Compiler::$_obj_call_regexp property
Smarty_Compiler::$_obj_ext_regexp property
Smarty_Compiler::$_obj_params_regexp property
Smarty_Compiler::$_obj_start_regexp property
Smarty_Compiler::$_parenth_param_regexp property
Smarty_Compiler::$_permitted_tokens property
Smarty_Compiler::$_plugin_info property
Smarty_Compiler::$_qstr_regexp property
Smarty_Compiler::$_reg_obj_regexp property
Smarty_Compiler::$_si_qstr_regexp property
Smarty_Compiler::$_strip_depth property
Smarty_Compiler::$_svar_regexp property
Smarty_Compiler::$_var_bracket_regexp property
Smarty_Compiler::$_var_regexp property
Smarty_Compiler::Smarty_Compiler function The class constructor.
Smarty_Compiler::_add_plugin function add plugin
Smarty_Compiler::_compile_arg_list function
Smarty_Compiler::_compile_block_tag function compile block function tag
Smarty_Compiler::_compile_capture_tag function Compile {capture} .. {/capture} tags
Smarty_Compiler::_compile_compiler_tag function compile the custom compiler tag
Smarty_Compiler::_compile_custom_tag function compile custom function tag
Smarty_Compiler::_compile_file function compile a resource
Smarty_Compiler::_compile_foreach_start function Compile {foreach ...} tag.
Smarty_Compiler::_compile_if_tag function Compile {if ...} tag
Smarty_Compiler::_compile_include_php_tag function Compile {include ...} tag
Smarty_Compiler::_compile_include_tag function Compile {include ...} tag
Smarty_Compiler::_compile_insert_tag function Compile {insert ...} tag
Smarty_Compiler::_compile_plugin_call function compiles call to plugin of type $type with name $name returns a string containing the function-name or method call without the paramter-list that would have follow to make the call valid php-syntax
Smarty_Compiler::_compile_registered_object_tag function compile a registered object tag
Smarty_Compiler::_compile_section_start function Compile {section ...} tag
Smarty_Compiler::_compile_smarty_ref function Compiles references of type $smarty.foo
Smarty_Compiler::_compile_tag function Compile a template tag
Smarty_Compiler::_expand_quoted_text function expand quoted text with embedded variables
Smarty_Compiler::_load_filters function load pre- and post-filters
Smarty_Compiler::_parse_attrs function Parse attribute string
Smarty_Compiler::_parse_conf_var function parse configuration variable expression into PHP code
Smarty_Compiler::_parse_is_expr function Parse is expression
Smarty_Compiler::_parse_modifiers function parse modifier chain into PHP code
Smarty_Compiler::_parse_parenth_args function parse arguments in function call parenthesis
Smarty_Compiler::_parse_section_prop function parse section property expression into PHP code
Smarty_Compiler::_parse_var function parse variable expression into PHP code
Smarty_Compiler::_parse_vars_props function compile multiple variables and section properties tokens into PHP code
Smarty_Compiler::_parse_var_props function compile single variable and section properties token into PHP code
Smarty_Compiler::_pop_cacheable_state function check if the compilation changes from non-cacheable to cacheable state with the end of the current plugin return php-code to reflect the transition.
Smarty_Compiler::_pop_tag function pop closing tag-name raise an error if this stack-top doesn't match with the closing tag
Smarty_Compiler::_push_cacheable_state function check if the compilation changes from cacheable to non-cacheable state with the beginning of the current plugin. return php-code to reflect the transition.
Smarty_Compiler::_push_tag function push opening tag-name, file-name and line-number on the tag-stack
Smarty_Compiler::_quote_replace function Quote subpattern references
Smarty_Compiler::_syntax_error function display Smarty syntax error