diff --git a/library/smarty/lexer/smarty_internal_templateparser.y b/library/smarty/lexer/smarty_internal_templateparser.y new file mode 100644 --- /dev/null +++ b/library/smarty/lexer/smarty_internal_templateparser.y @@ -0,0 +1,1329 @@ +/** +* Smarty Internal Plugin Templateparser +* +* This is the template parser +* +* +* @package Smarty +* @subpackage Compiler +* @author Uwe Tews +*/ +%stack_size 500 +%name TP_ +%declare_class { +/** +* Smarty Internal Plugin Templateparser +* +* This is the template parser. +* It is generated from the smarty_internal_templateparser.y file +* @package Smarty +* @subpackage Compiler +* @author Uwe Tews +*/ +class Smarty_Internal_Templateparser +} +%include_class +{ + const Err1 = "Security error: Call to private object member not allowed"; + const Err2 = "Security error: Call to dynamic object member not allowed"; + const Err3 = "PHP in template not allowed. Use SmartyBC to enable it"; + + /** + * result status + * + * @var bool + */ + public $successful = true; + /** + * return value + * + * @var mixed + */ + public $retvalue = 0; + /** + * counter for prefix code + * + * @var int + */ + public static $prefix_number = 0; + /** + * @var + */ + public $yymajor; + /** + * last index of array variable + * + * @var mixed + */ + public $last_index; + /** + * last variable name + * + * @var string + */ + public $last_variable; + /** + * root parse tree buffer + * + * @var Smarty_Internal_ParseTree + */ + public $root_buffer; + /** + * current parse tree object + * + * @var Smarty_Internal_ParseTree + */ + public $current_buffer; + /** + * lexer object + * + * @var Smarty_Internal_Templatelexer + */ + private $lex; + /** + * internal error flag + * + * @var bool + */ + private $internalError = false; + /** + * {strip} status + * + * @var bool + */ + public $strip = false; + /** + * compiler object + * + * @var Smarty_Internal_TemplateCompilerBase + */ + public $compiler = null; + /** + * smarty object + * + * @var Smarty + */ + public $smarty = null; + /** + * template object + * + * @var Smarty_Internal_Template + */ + public $template = null; + /** + * block nesting level + * + * @var int + */ + public $block_nesting_level = 0; + + /** + * security object + * + * @var Smarty_Security + */ + private $security = null; + + /** + * constructor + * + * @param Smarty_Internal_Templatelexer $lex + * @param Smarty_Internal_TemplateCompilerBase $compiler + */ + function __construct(Smarty_Internal_Templatelexer $lex, Smarty_Internal_TemplateCompilerBase $compiler) + { + $this->lex = $lex; + $this->compiler = $compiler; + $this->template = $this->compiler->template; + $this->smarty = $this->template->smarty; + $this->security = isset($this->smarty->security_policy) ? $this->smarty->security_policy : false; + $this->current_buffer = $this->root_buffer = new Smarty_Internal_ParseTree_Template($this); + } + + /** + * insert PHP code in current buffer + * + * @param string $code + */ + public function insertPhpCode($code) + { + $this->current_buffer->append_subtree(new Smarty_Internal_ParseTree_Tag($this, $code)); + } + + /** + * merge PHP code with prefix code and return parse tree tag object + * + * @param string $code + * + * @return Smarty_Internal_ParseTree_Tag + */ + public function mergePrefixCode($code) + { + $tmp =''; + foreach ($this->compiler->prefix_code as $preCode) { + $tmp = empty($tmp) ? $preCode : $this->compiler->appendCode($tmp, $preCode); + } + $this->compiler->prefix_code=array(); + $tmp = empty($tmp) ? $code : $this->compiler->appendCode($tmp, $code); + return new Smarty_Internal_ParseTree_Tag($this, $this->compiler->processNocacheCode($tmp,true)); + } + +} + +%token_prefix TP_ + +%parse_accept +{ + $this->successful = !$this->internalError; + $this->internalError = false; + $this->retvalue = $this->_retvalue; +} + +%syntax_error +{ + $this->internalError = true; + $this->yymajor = $yymajor; + $this->compiler->trigger_template_error(); +} + +%stack_overflow +{ + $this->internalError = true; + $this->compiler->trigger_template_error("Stack overflow in template parser"); +} + +%left VERT. +%left COLON. + + // + // complete template + // +start(res) ::= template. { + res = $this->root_buffer->to_smarty_php(); +} + + // + // loop over template elements + // + // single template element +template ::= template_element(e). { + if (e != null) { + $this->current_buffer->append_subtree(e); + } +} + + // loop of elements +template ::= template template_element(e). { + if (e != null) { + // because of possible code injection + $this->current_buffer->append_subtree(e); + } +} + + // empty template +template ::= . + +// +// template elements +// + // Smarty tag +template_element(res)::= smartytag(st). { + if ($this->compiler->has_code) { + res = $this->mergePrefixCode(st); + } else { + res = null; + } + $this->compiler->has_variable_string = false; + $this->block_nesting_level = count($this->compiler->_tag_stack); +} + + // Literal +template_element(res) ::= literal(l). { + res = new Smarty_Internal_ParseTree_Text($this, l); +} + // php tags +template_element(res)::= PHP(o). { + $code = $this->compiler->compileTag('private_php',array(array('code' => o), array('type' => $this->lex->phpType )),array()); + if ($this->compiler->has_code && !empty($code)) { + $tmp =''; foreach ($this->compiler->prefix_code as $code) {$tmp.=$code;} $this->compiler->prefix_code=array(); + res = new Smarty_Internal_ParseTree_Tag($this, $this->compiler->processNocacheCode($tmp.$code,true)); + } else { + res = null; + } +} + + // nocache code +template_element(res)::= NOCACHE(c). { + $this->compiler->tag_nocache = true; + $save = $this->template->has_nocache_code; + res = new Smarty_Internal_ParseTree_Tag($this, $this->compiler->processNocacheCode("\n", $this->compiler, true)); + $this->template->has_nocache_code = $save; +} + // template text +template_element(res)::= text_content(t). { + res = $this->compiler->processText(t); +} + +text_content(res) ::= TEXT(o). { + res = o; +} + +text_content(res) ::= text_content(t) TEXT(o). { + res = t . o; +} + + // strip on +template_element ::= STRIPON(d). { + $this->strip = true; +} + // strip off +template_element ::= STRIPOFF(d). { + $this->strip = false; +} + // process source of inheritance child block +template_element ::= BLOCKSOURCE(s). { + if ($this->strip) { + SMARTY_INTERNAL_COMPILE_BLOCK::blockSource($this->compiler, preg_replace('![\t ]*[\r\n]+[\t ]*!', '', s)); + } else { + SMARTY_INTERNAL_COMPILE_BLOCK::blockSource($this->compiler, s); + } +} + + // Litteral +literal(res) ::= LITERALSTART LITERALEND. { + res = ''; +} + +literal(res) ::= LITERALSTART literal_elements(l) LITERALEND. { + res = l; +} + +literal_elements(res) ::= literal_elements(l1) literal_element(l2). { + res = l1.l2; +} + +literal_elements(res) ::= . { + res = ''; +} + +literal_element(res) ::= literal(l). { + res = l; +} + +literal_element(res) ::= LITERAL(l). { + res = l; +} + +smartytag(res) ::= tag(t) RDEL. { + res = t; +} +// +// output tags start here +// +smartytag(res) ::= SIMPLEOUTPUT(i). { + $var = trim(substr(i, $this->lex->ldel_length, -$this->lex->rdel_length), ' $'); + if (preg_match('/^(.*)(\s+nocache)$/', $var, $match)) { + res = $this->compiler->compileTag('private_print_expression',array('nocache'),array('value'=>$this->compiler->compileVariable('\''.$match[1].'\''))); + } else { + res = $this->compiler->compileTag('private_print_expression',array(),array('value'=>$this->compiler->compileVariable('\''.$var.'\''))); + } +} + + // output with optional attributes +tag(res) ::= LDEL variable(e). { + res = $this->compiler->compileTag('private_print_expression',array(),array('value'=>e)); +} + +tag(res) ::= LDEL variable(e) attributes(a). { + res = $this->compiler->compileTag('private_print_expression',a,array('value'=>e)); +} +tag(res) ::= LDEL value(e). { + res = $this->compiler->compileTag('private_print_expression',array(),array('value'=>e)); +} +tag(res) ::= LDEL value(e) attributes(a). { + res = $this->compiler->compileTag('private_print_expression',a,array('value'=>e)); +} + +tag(res) ::= LDEL expr(e). { + res = $this->compiler->compileTag('private_print_expression',array(),array('value'=>e)); +} + +tag(res) ::= LDEL expr(e) attributes(a). { + res = $this->compiler->compileTag('private_print_expression',a,array('value'=>e)); +} + +// +// Smarty tags start here +// + + // assign new style +tag(res) ::= LDEL DOLLARID(i) EQUAL value(e). { + res = $this->compiler->compileTag('assign',array(array('value'=>e),array('var'=>'\''.substr(i,1).'\''))); +} + +tag(res) ::= LDEL DOLLARID(i) EQUAL expr(e). { + res = $this->compiler->compileTag('assign',array(array('value'=>e),array('var'=>'\''.substr(i,1).'\''))); +} + +tag(res) ::= LDEL DOLLARID(i) EQUAL expr(e) attributes(a). { + res = $this->compiler->compileTag('assign',array_merge(array(array('value'=>e),array('var'=>'\''.substr(i,1).'\'')),a)); +} + +tag(res) ::= LDEL varindexed(vi) EQUAL expr(e) attributes(a). { + res = $this->compiler->compileTag('assign',array_merge(array(array('value'=>e),array('var'=>vi['var'])),a),array('smarty_internal_index'=>vi['smarty_internal_index'])); +} + +// simple tag like {name} +smartytag(res)::= SIMPLETAG(t). { + $tag = trim(substr(t, $this->lex->ldel_length, -$this->lex->rdel_length)); + if ($tag == 'strip') { + $this->strip = true; + res = null;; + } else { + if (defined($tag)) { + if ($this->security) { + $this->security->isTrustedConstant($tag, $this->compiler); + } + res = $this->compiler->compileTag('private_print_expression',array(),array('value'=>$tag)); + } else { + if (preg_match('/^(.*)(\s+nocache)$/', $tag, $match)) { + res = $this->compiler->compileTag($match[1],array("'nocache'")); + } else { + res = $this->compiler->compileTag($tag,array()); + } + } + } +} + + // tag with optional Smarty2 style attributes +tag(res) ::= LDEL ID(i) attributes(a). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = $this->compiler->compileTag('private_print_expression',a,array('value'=>i)); + } else { + res = $this->compiler->compileTag(i,a); + } +} +tag(res) ::= LDEL ID(i). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = $this->compiler->compileTag('private_print_expression',array(),array('value'=>i)); + } else { + res = $this->compiler->compileTag(i,array()); + } +} + + + // tag with modifier and optional Smarty2 style attributes +tag(res) ::= LDEL ID(i) modifierlist(l)attributes(a). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = $this->compiler->compileTag('private_print_expression',a,array('value'=>i, 'modifierlist'=>l)); + } else { + res = ''.$this->compiler->compileTag(i,a).'compiler->compileTag('private_modifier',array(),array('modifierlist'=>l,'value'=>'ob_get_clean()')).';?>'; + } +} + + // registered object tag +tag(res) ::= LDEL ID(i) PTR ID(m) attributes(a). { + res = $this->compiler->compileTag(i,a,array('object_method'=>m)); +} + + // registered object tag with modifiers +tag(res) ::= LDEL ID(i) PTR ID(me) modifierlist(l) attributes(a). { + res = ''.$this->compiler->compileTag(i,a,array('object_method'=>me)).'compiler->compileTag('private_modifier',array(),array('modifierlist'=>l,'value'=>'ob_get_clean()')).';?>'; +} + + // {if}, {elseif} and {while} tag +tag(res) ::= LDELIF(i) expr(ie). { + $tag = trim(substr(i,$this->lex->ldel_length)); + res = $this->compiler->compileTag(($tag == 'else if')? 'elseif' : $tag,array(),array('if condition'=>ie)); +} + +tag(res) ::= LDELIF(i) expr(ie) attributes(a). { + $tag = trim(substr(i,$this->lex->ldel_length)); + res = $this->compiler->compileTag(($tag == 'else if')? 'elseif' : $tag,a,array('if condition'=>ie)); +} + +tag(res) ::= LDELIF(i) statement(ie). { + $tag = trim(substr(i,$this->lex->ldel_length)); + res = $this->compiler->compileTag(($tag == 'else if')? 'elseif' : $tag,array(),array('if condition'=>ie)); +} + +tag(res) ::= LDELIF(i) statement(ie) attributes(a). { + $tag = trim(substr(i,$this->lex->ldel_length)); + res = $this->compiler->compileTag(($tag == 'else if')? 'elseif' : $tag,a,array('if condition'=>ie)); +} + + // {for} tag +tag(res) ::= LDELFOR statements(st) SEMICOLON expr(ie) SEMICOLON varindexed(v2) foraction(e2) attributes(a). { + res = $this->compiler->compileTag('for',array_merge(a,array(array('start'=>st),array('ifexp'=>ie),array('var'=>v2),array('step'=>e2))),1); +} + + foraction(res) ::= EQUAL expr(e). { + res = '='.e; +} + + foraction(res) ::= INCDEC(e). { + res = e; +} + +tag(res) ::= LDELFOR statement(st) TO expr(v) attributes(a). { + res = $this->compiler->compileTag('for',array_merge(a,array(array('start'=>st),array('to'=>v))),0); +} + +tag(res) ::= LDELFOR statement(st) TO expr(v) STEP expr(v2) attributes(a). { + res = $this->compiler->compileTag('for',array_merge(a,array(array('start'=>st),array('to'=>v),array('step'=>v2))),0); +} + + // {foreach} tag +tag(res) ::= LDELFOREACH attributes(a). { + res = $this->compiler->compileTag('foreach',a); +} + + // {foreach $array as $var} tag +tag(res) ::= LDELFOREACH SPACE value(v1) AS varvar(v0) attributes(a). { + res = $this->compiler->compileTag('foreach',array_merge(a,array(array('from'=>v1),array('item'=>v0)))); +} + +tag(res) ::= LDELFOREACH SPACE value(v1) AS varvar(v2) APTR varvar(v0) attributes(a). { + res = $this->compiler->compileTag('foreach',array_merge(a,array(array('from'=>v1),array('item'=>v0),array('key'=>v2)))); +} + +tag(res) ::= LDELFOREACH SPACE expr(e) AS varvar(v0) attributes(a). { + res = $this->compiler->compileTag('foreach',array_merge(a,array(array('from'=>e),array('item'=>v0)))); +} + +tag(res) ::= LDELFOREACH SPACE expr(e) AS varvar(v1) APTR varvar(v0) attributes(a). { + res = $this->compiler->compileTag('foreach',array_merge(a,array(array('from'=>e),array('item'=>v0),array('key'=>v1)))); +} + + // {setfilter} +tag(res) ::= LDELSETFILTER ID(m) modparameters(p). { + res = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array(array_merge(array(m),p)))); +} + +tag(res) ::= LDELSETFILTER ID(m) modparameters(p) modifierlist(l). { + res = $this->compiler->compileTag('setfilter',array(),array('modifier_list'=>array_merge(array(array_merge(array(m),p)),l))); +} + + // {$smarty.block.child} or {$smarty.block.parent} +tag(res) ::= LDEL SMARTYBLOCKCHILDPARENT(i). { + $j = strrpos(i,'.'); + if (i[$j+1] == 'c') { + // {$smarty.block.child} + res = SMARTY_INTERNAL_COMPILE_BLOCK::compileChildBlock($this->compiler); + } else { + // {$smarty.block.parent} + res = SMARTY_INTERNAL_COMPILE_BLOCK::compileParentBlock($this->compiler); + } +} + + + // end of block tag {/....} +smartytag(res)::= CLOSETAG(t). { + $tag = trim(substr(t, $this->lex->ldel_length, -$this->lex->rdel_length), ' /'); + if ($tag == 'strip') { + $this->strip = false; + res = null; + } else { + res = $this->compiler->compileTag($tag.'close',array()); + } + } +tag(res) ::= LDELSLASH ID(i). { + res = $this->compiler->compileTag(i.'close',array()); +} + +tag(res) ::= LDELSLASH ID(i) modifierlist(l). { + res = $this->compiler->compileTag(i.'close',array(),array('modifier_list'=>l)); +} + + // end of block object tag {/....} +tag(res) ::= LDELSLASH ID(i) PTR ID(m). { + res = $this->compiler->compileTag(i.'close',array(),array('object_method'=>m)); +} + +tag(res) ::= LDELSLASH ID(i) PTR ID(m) modifierlist(l). { + res = $this->compiler->compileTag(i.'close',array(),array('object_method'=>m, 'modifier_list'=>l)); +} + +// +//Attributes of Smarty tags +// + // list of attributes +attributes(res) ::= attributes(a1) attribute(a2). { + res = a1; + res[] = a2; +} + + // single attribute +attributes(res) ::= attribute(a). { + res = array(a); +} + + // no attributes +attributes(res) ::= . { + res = array(); +} + + // attribute +attribute(res) ::= SPACE ID(v) EQUAL ID(id). { + if (defined(id)) { + if ($this->security) { + $this->security->isTrustedConstant(id, $this->compiler); + } + res = array(v=>id); + } else { + res = array(v=>'\''.id.'\''); + } +} + +attribute(res) ::= ATTR(v) expr(e). { + res = array(trim(v," =\n\r\t")=>e); +} + +attribute(res) ::= ATTR(v) value(e). { + res = array(trim(v," =\n\r\t")=>e); +} + +attribute(res) ::= SPACE ID(v). { + res = '\''.v.'\''; +} + +attribute(res) ::= SPACE expr(e). { + res = e; +} + +attribute(res) ::= SPACE value(v). { + res = v; +} + +attribute(res) ::= SPACE INTEGER(i) EQUAL expr(e). { + res = array(i=>e); +} + + + +// +// statement +// +statements(res) ::= statement(s). { + res = array(s); +} + +statements(res) ::= statements(s1) COMMA statement(s). { + s1[]=s; + res = s1; +} + +statement(res) ::= DOLLARID(i) EQUAL INTEGER(e). { + res = array('var' => '\''.substr(i,1).'\'', 'value'=>e); +} +statement(res) ::= DOLLARID(i) EQUAL expr(e). { + res = array('var' => '\''.substr(i,1).'\'', 'value'=>e); +} + +statement(res) ::= varindexed(vi) EQUAL expr(e). { + res = array('var' => vi, 'value'=>e); +} + +statement(res) ::= OPENP statement(st) CLOSEP. { + res = st; +} + + +// +// expressions +// + + // single value +expr(res) ::= value(v). { + res = v; +} + + // ternary +expr(res) ::= ternary(v). { + res = v; +} + + // resources/streams +expr(res) ::= DOLLARID(i) COLON ID(i2). { + res = '$_smarty_tpl->getStreamVariable(\''.substr(i,1).'://' . i2 . '\')'; +} + + // arithmetic expression +expr(res) ::= expr(e) MATH(m) value(v). { + res = e . trim(m) . v; +} + +expr(res) ::= expr(e) UNIMATH(m) value(v). { + res = e . trim(m) . v; +} + + // array +expr(res) ::= array(a). { + res = a; +} + + // modifier +expr(res) ::= expr(e) modifierlist(l). { + res = $this->compiler->compileTag('private_modifier',array(),array('value'=>e,'modifierlist'=>l)); +} + +// if expression + // simple expression +expr(res) ::= expr(e1) lop(c) expr(e2). { + res = (isset(c['pre']) ? c['pre'] : '') . e1.c['op'].e2 . (isset(c['pre']) ? ')' : ''); +} +expr(res) ::= expr(e1) scond(c). { + res = c . e1 . ')'; +} + +expr(res) ::= expr(e1) ISIN array(a). { + res = 'in_array('.e1.','.a.')'; +} + +expr(res) ::= expr(e1) ISIN value(v). { + res = 'in_array('.e1.',(array)'.v.')'; +} + +expr(res) ::= variable(v1) INSTANCEOF(i) ns1(v2). { + res = v1.i.v2; +} + + +// +// ternary +// +ternary(res) ::= OPENP expr(v) CLOSEP QMARK DOLLARID(e1) COLON expr(e2). { + res = v.' ? '. $this->compiler->compileVariable('\''.substr(e1,1).'\'') . ' : '.e2; +} + +ternary(res) ::= OPENP expr(v) CLOSEP QMARK expr(e1) COLON expr(e2). { + res = v.' ? '.e1.' : '.e2; +} + + // value +value(res) ::= variable(v). { + res = v; +} + + // +/- value +value(res) ::= UNIMATH(m) value(v). { + res = m.v; +} + + // logical negation +value(res) ::= NOT value(v). { + res = '!'.v; +} + +value(res) ::= TYPECAST(t) value(v). { + res = t.v; +} + +value(res) ::= variable(v) INCDEC(o). { + res = v.o; +} + + // numeric +value(res) ::= HEX(n). { + res = n; +} + +value(res) ::= INTEGER(n). { + res = n; +} + +value(res) ::= INTEGER(n1) DOT INTEGER(n2). { + res = n1.'.'.n2; +} + +value(res) ::= INTEGER(n1) DOT. { + res = n1.'.'; +} + +value(res) ::= DOT INTEGER(n1). { + res = '.'.n1; +} + + // ID, true, false, null +value(res) ::= ID(id). { + if (defined(id)) { + if ($this->security) { + $this->security->isTrustedConstant(id, $this->compiler); + } + res = id; + } else { + res = '\''.id.'\''; + } +} + + // function call +value(res) ::= function(f). { + res = f; +} + + // expression +value(res) ::= OPENP expr(e) CLOSEP. { + res = "(". e .")"; +} + + // singele quoted string +value(res) ::= SINGLEQUOTESTRING(t). { + res = t; +} + + // double quoted string +value(res) ::= doublequoted_with_quotes(s). { + res = s; +} + + +value(res) ::= varindexed(vi) DOUBLECOLON static_class_access(r). { + self::$prefix_number++; + if (vi['var'] == '\'smarty\'') { + $this->compiler->prefix_code[] = 'compiler->compileTag('private_special_variable',array(),vi['smarty_internal_index']).';?>'; + } else { + $this->compiler->prefix_code[] = 'compiler->compileVariable(vi['var']).vi['smarty_internal_index'].';?>'; + } + res = '$_tmp'.self::$prefix_number.'::'.r[0].r[1]; +} + + // Smarty tag +value(res) ::= smartytag(st). { + self::$prefix_number++; + $tmp = $this->compiler->appendCode('', st); + $this->compiler->prefix_code[] = $this->compiler->appendCode($tmp, ''); + res = '$_tmp'.self::$prefix_number; +} + +value(res) ::= value(v) modifierlist(l). { + res = $this->compiler->compileTag('private_modifier',array(),array('value'=>v,'modifierlist'=>l)); +} + // name space constant +value(res) ::= NAMESPACE(c). { + res = c; +} + + + // static class access +value(res) ::= ns1(c)DOUBLECOLON static_class_access(s). { + if (!in_array(strtolower(c), array('self', 'parent')) && (!$this->security || $this->security->isTrustedStaticClassAccess(c, s, $this->compiler))) { + if (isset($this->smarty->registered_classes[c])) { + res = $this->smarty->registered_classes[c].'::'.s[0].s[1]; + } else { + res = c.'::'.s[0].s[1]; + } + } else { + $this->compiler->trigger_template_error ("static class '".c."' is undefined or not allowed by security setting"); + } +} +// +// namespace stuff +// + +ns1(res) ::= ID(i). { + res = i; +} + +ns1(res) ::= NAMESPACE(i). { + res = i; +} + +//ns1(res) ::= variable(v). { +// res = v; +//} + + + + +// +// variables +// + // Smarty variable (optional array) +variable(res) ::= DOLLARID(i). { + res = $this->compiler->compileVariable('\''.substr(i,1).'\''); +} +variable(res) ::= varindexed(vi). { + if (vi['var'] == '\'smarty\'') { + $smarty_var = $this->compiler->compileTag('private_special_variable',array(),vi['smarty_internal_index']); + res = $smarty_var; + } else { + // used for array reset,next,prev,end,current + $this->last_variable = vi['var']; + $this->last_index = vi['smarty_internal_index']; + res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index']; + } +} + + // variable with property +variable(res) ::= varvar(v) AT ID(p). { + res = '$_smarty_tpl->tpl_vars['. v .']->'.p; +} + + // object +variable(res) ::= object(o). { + res = o; +} + + // config variable +variable(res) ::= HATCH ID(i) HATCH. { + res = '$_smarty_tpl->getConfigVariable( \''. i .'\')'; +} + +variable(res) ::= HATCH ID(i) HATCH arrayindex(a). { + res = '(is_array($tmp = $_smarty_tpl->getConfigVariable( \''. i .'\')) ? $tmp'.a.' :null)'; +} + +variable(res) ::= HATCH variable(v) HATCH. { + res = '$_smarty_tpl->getConfigVariable( '. v .')'; +} + +variable(res) ::= HATCH variable(v) HATCH arrayindex(a). { + res = '(is_array($tmp = $_smarty_tpl->getConfigVariable( '. v .')) ? $tmp'.a.' : null)'; +} + +varindexed(res) ::= DOLLARID(i) arrayindex(a). { + res = array('var'=>'\''.substr(i,1).'\'', 'smarty_internal_index'=>a); +} +varindexed(res) ::= varvar(v) arrayindex(a). { + res = array('var'=>v, 'smarty_internal_index'=>a); +} + +// +// array index +// + // multiple array index +arrayindex(res) ::= arrayindex(a1) indexdef(a2). { + res = a1.a2; +} + + // no array index +arrayindex ::= . { + return; +} + +// single index definition + // Smarty2 style index +indexdef(res) ::= DOT DOLLARID(i). { + res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']'; +} +indexdef(res) ::= DOT varvar(v). { + res = '['.$this->compiler->compileVariable(v).']'; +} + +indexdef(res) ::= DOT varvar(v) AT ID(p). { + res = '['.$this->compiler->compileVariable(v).'->'.p.']'; +} + +indexdef(res) ::= DOT ID(i). { + if (defined(i)) { + if ($this->security) { + $this->security->isTrustedConstant(i, $this->compiler); + } + res = '['. i .']'; + } else { + res = "['". i ."']"; + } +} + +indexdef(res) ::= DOT INTEGER(n). { + res = '['. n .']'; +} + +indexdef(res) ::= DOT LDEL expr(e) RDEL. { + res = '['. e .']'; +} + + // section tag index +indexdef(res) ::= OPENB ID(i)CLOSEB. { + res = '['.$this->compiler->compileTag('private_special_variable',array(),'[\'section\'][\''.i.'\'][\'index\']').']'; +} + +indexdef(res) ::= OPENB ID(i) DOT ID(i2) CLOSEB. { + res = '['.$this->compiler->compileTag('private_special_variable',array(),'[\'section\'][\''.i.'\'][\''.i2.'\']').']'; +} +indexdef(res) ::= OPENB SINGLEQUOTESTRING(s) CLOSEB. { + res = '['.s.']'; +} +indexdef(res) ::= OPENB INTEGER(n) CLOSEB. { + res = '['.n.']'; +} +indexdef(res) ::= OPENB DOLLARID(i) CLOSEB. { + res = '['.$this->compiler->compileVariable('\''.substr(i,1).'\'').']';; +} +indexdef(res) ::= OPENB variable(v) CLOSEB. { + res = '['.v.']'; +} +indexdef(res) ::= OPENB value(v) CLOSEB. { + res = '['.v.']'; +} + + // PHP style index +indexdef(res) ::= OPENB expr(e) CLOSEB. { + res = '['. e .']'; +} + + // for assign append array +indexdef(res) ::= OPENB CLOSEB. { + res = '[]'; +} + + +// +// variable variable names +// + + // singel identifier element +varvar(res) ::= DOLLARID(i). { + res = '\''.substr(i,1).'\''; +} + // single $ +varvar(res) ::= DOLLAR. { + res = "''"; +} + + // sequence of identifier elements +varvar(res) ::= varvar(v1) varvarele(v2). { + res = v1.'.'.v2; +} + + // fix sections of element +varvarele(res) ::= ID(s). { + res = '\''.s.'\''; +} + + // variable sections of element +varvarele(res) ::= LDEL expr(e) RDEL. { + res = '('.e.')'; +} + +// +// objects +// +object(res) ::= varindexed(vi) objectchain(oc). { + if (vi['var'] == '\'smarty\'') { + res = $this->compiler->compileTag('private_special_variable',array(),vi['smarty_internal_index']).oc; + } else { + res = $this->compiler->compileVariable(vi['var']).vi['smarty_internal_index'].oc; + } +} + + // single element +objectchain(res) ::= objectelement(oe). { + res = oe; +} + + // chain of elements +objectchain(res) ::= objectchain(oc) objectelement(oe). { + res = oc.oe; +} + + // variable +objectelement(res)::= PTR ID(i) arrayindex(a). { + if ($this->security && substr(i,0,1) == '_') { + $this->compiler->trigger_template_error (self::Err1); + } + res = '->'.i.a; +} + +objectelement(res)::= PTR varvar(v) arrayindex(a). { + if ($this->security) { + $this->compiler->trigger_template_error (self::Err2); + } + res = '->{'.$this->compiler->compileVariable(v).a.'}'; +} + +objectelement(res)::= PTR LDEL expr(e) RDEL arrayindex(a). { + if ($this->security) { + $this->compiler->trigger_template_error (self::Err2); + } + res = '->{'.e.a.'}'; +} + +objectelement(res)::= PTR ID(ii) LDEL expr(e) RDEL arrayindex(a). { + if ($this->security) { + $this->compiler->trigger_template_error (self::Err2); + } + res = '->{\''.ii.'\'.'.e.a.'}'; +} + + // method +objectelement(res)::= PTR method(f). { + res = '->'.f; +} + + +// +// function +// +function(res) ::= ns1(f) OPENP params(p) CLOSEP. { + if (!$this->security || $this->security->isTrustedPhpFunction(f, $this->compiler)) { + if (strcasecmp(f,'isset') === 0 || strcasecmp(f,'empty') === 0 || strcasecmp(f,'array') === 0 || is_callable(f)) { + $func_name = strtolower(f); + if ($func_name == 'isset') { + if (count(p) == 0) { + $this->compiler->trigger_template_error ('Illegal number of paramer in "isset()"'); + } + $par = implode(',',p); + if (strncasecmp($par,'$_smarty_tpl->getConfigVariable',strlen('$_smarty_tpl->getConfigVariable')) === 0) { + self::$prefix_number++; + $this->compiler->prefix_code[] = ''; + $isset_par = '$_tmp'.self::$prefix_number; + } else { + $isset_par=str_replace("')->value","',null,true,false)->value",$par); + } + res = f . "(". $isset_par .")"; + } elseif (in_array($func_name,array('empty','reset','current','end','prev','next'))){ + if (count(p) != 1) { + $this->compiler->trigger_template_error ('Illegal number of paramer in "empty()"'); + } + if ($func_name == 'empty') { + res = $func_name.'('.str_replace("')->value","',null,true,false)->value",p[0]).')'; + } else { + res = $func_name.'('.p[0].')'; + } + } else { + res = f . "(". implode(',',p) .")"; + } + } else { + $this->compiler->trigger_template_error ("unknown function \"" . f . "\""); + } + } +} + + +// +// method +// +method(res) ::= ID(f) OPENP params(p) CLOSEP. { + if ($this->security && substr(f,0,1) == '_') { + $this->compiler->trigger_template_error (self::Err1); + } + res = f . "(". implode(',',p) .")"; +} + +method(res) ::= DOLLARID(f) OPENP params(p) CLOSEP. { + if ($this->security) { + $this->compiler->trigger_template_error (self::Err2); + } + self::$prefix_number++; + $this->compiler->prefix_code[] = 'compiler->compileVariable('\''.substr(f,1).'\'').';?>'; + res = '$_tmp'.self::$prefix_number.'('. implode(',',p) .')'; +} + +// function/method parameter + // multiple parameters +params(res) ::= params(p) COMMA expr(e). { + res = array_merge(p,array(e)); +} + + // single parameter +params(res) ::= expr(e). { + res = array(e); +} + + // kein parameter +params(res) ::= . { + res = array(); +} + +// +// modifier +// +modifierlist(res) ::= modifierlist(l) modifier(m) modparameters(p). { + res = array_merge(l,array(array_merge(m,p))); +} + +modifierlist(res) ::= modifier(m) modparameters(p). { + res = array(array_merge(m,p)); +} + +modifier(res) ::= VERT AT ID(m). { + res = array(m); +} + +modifier(res) ::= VERT ID(m). { + res = array(m); +} + +// +// modifier parameter +// + // multiple parameter +modparameters(res) ::= modparameters(mps) modparameter(mp). { + res = array_merge(mps,mp); +} + + // no parameter +modparameters(res) ::= . { + res = array(); +} + + // parameter expression +modparameter(res) ::= COLON value(mp). { + res = array(mp); +} + +modparameter(res) ::= COLON array(mp). { + res = array(mp); +} + + // static class methode call +static_class_access(res) ::= method(m). { + res = array(m, '', 'method'); +} + + // static class methode call with object chainig +static_class_access(res) ::= method(m) objectchain(oc). { + res = array(m, oc, 'method'); +} + + // static class constant +static_class_access(res) ::= ID(v). { + res = array(v, ''); +} + + // static class variables +static_class_access(res) ::= DOLLARID(v) arrayindex(a). { + res = array(v, a, 'property'); +} + + // static class variables with object chain +static_class_access(res) ::= DOLLARID(v) arrayindex(a) objectchain(oc). { + res = array(v, a.oc, 'property'); +} + + +// if conditions and operators +lop(res) ::= LOGOP(o). { + res['op'] = ' '. trim(o) . ' '; +} + +lop(res) ::= TLOGOP(o). { + static $lops = array( + 'eq' => array('op' => ' == ', 'pre' => null), + 'ne' => array('op' => ' != ', 'pre' => null), + 'neq' => array('op' => ' != ', 'pre' => null), + 'gt' => array('op' => ' > ', 'pre' => null), + 'ge' => array('op' => ' >= ', 'pre' => null), + 'gte' => array('op' => ' >= ', 'pre' => null), + 'lt' => array('op' => ' < ', 'pre' => null), + 'le' => array('op' => ' <= ', 'pre' => null), + 'lte' => array('op' => ' <= ', 'pre' => null), + 'mod' => array('op' => ' % ', 'pre' => null), + 'and' => array('op' => ' && ', 'pre' => null), + 'or' => array('op' => ' || ', 'pre' => null), + 'xor' => array('op' => ' xor ', 'pre' => null), + 'isdivby' => array('op' => ' % ', 'pre' => '!('), + 'isnotdivby' => array('op' => ' % ', 'pre' => '('), + 'isevenby' => array('op' => ' / ', 'pre' => '!(1 & '), + 'isnotevenby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isoddby' => array('op' => ' / ', 'pre' => '(1 & '), + 'isnotoddby' => array('op' => ' / ', 'pre' => '!(1 & '), + ); + $op = strtolower(preg_replace('/\s*/', '', o)); + res = $lops[$op]; +} + +scond(res) ::= SINGLECOND(o). { + static $scond = array ( + 'iseven' => '!(1 & ', + 'isnoteven' => '(1 & ', + 'isodd' => '(1 & ', + 'isnotodd' => '!(1 & ', + ); + $op = strtolower(str_replace(' ', '', o)); + res = $scond[$op]; +} + +// +// ARRAY element assignment +// +array(res) ::= OPENB arrayelements(a) CLOSEB. { + res = 'array('.a.')'; +} + +arrayelements(res) ::= arrayelement(a). { + res = a; +} + +arrayelements(res) ::= arrayelements(a1) COMMA arrayelement(a). { + res = a1.','.a; +} + +arrayelements ::= . { + return; +} + +arrayelement(res) ::= value(e1) APTR expr(e2). { + res = e1.'=>'.e2; +} + +arrayelement(res) ::= ID(i) APTR expr(e2). { + res = '\''.i.'\'=>'.e2; +} + +arrayelement(res) ::= expr(e). { + res = e; +} + + +// +// double qouted strings +// +doublequoted_with_quotes(res) ::= QUOTE QUOTE. { + res = "''"; +} + +doublequoted_with_quotes(res) ::= QUOTE doublequoted(s) QUOTE. { + res = s->to_smarty_php(); +} + + +doublequoted(res) ::= doublequoted(o1) doublequotedcontent(o2). { + o1->append_subtree(o2); + res = o1; +} + +doublequoted(res) ::= doublequotedcontent(o). { + res = new Smarty_Internal_ParseTree_Dq($this, o); +} + +doublequotedcontent(res) ::= BACKTICK variable(v) BACKTICK. { + res = new Smarty_Internal_ParseTree_Code($this, '(string)'.v); +} + +doublequotedcontent(res) ::= BACKTICK expr(e) BACKTICK. { + res = new Smarty_Internal_ParseTree_Code($this, '(string)'.e); +} + +doublequotedcontent(res) ::= DOLLARID(i). { + res = new Smarty_Internal_ParseTree_Code($this, '(string)$_smarty_tpl->tpl_vars[\''. substr(i,1) .'\']->value'); +} + +doublequotedcontent(res) ::= LDEL variable(v) RDEL. { + res = new Smarty_Internal_ParseTree_Code($this, '(string)'.v); +} + +doublequotedcontent(res) ::= LDEL expr(e) RDEL. { + res = new Smarty_Internal_ParseTree_Code($this, '(string)('.e.')'); +} + +doublequotedcontent(res) ::= smartytag(st). { + res = new Smarty_Internal_ParseTree_Tag($this, st); +} + +doublequotedcontent(res) ::= TEXT(o). { + res = new Smarty_Internal_ParseTree_DqContent($this, o); +} +