|
1 <?php |
|
2 /** |
|
3 * Smarty Internal Plugin CompileBase |
|
4 * |
|
5 * @package Smarty |
|
6 * @subpackage Compiler |
|
7 * @author Uwe Tews |
|
8 */ |
|
9 |
|
10 /** |
|
11 * This class does extend all internal compile plugins |
|
12 * |
|
13 * @package Smarty |
|
14 * @subpackage Compiler |
|
15 */ |
|
16 abstract class Smarty_Internal_CompileBase { |
|
17 /** |
|
18 * Array of names of required attribute required by tag |
|
19 * |
|
20 * @var array |
|
21 */ |
|
22 public $required_attributes = array(); |
|
23 /** |
|
24 * Array of names of optional attribute required by tag |
|
25 * use array('_any') if there is no restriction of attributes names |
|
26 * |
|
27 * @var array |
|
28 */ |
|
29 public $optional_attributes = array(); |
|
30 /** |
|
31 * Shorttag attribute order defined by its names |
|
32 * |
|
33 * @var array |
|
34 */ |
|
35 public $shorttag_order = array(); |
|
36 /** |
|
37 * Array of names of valid option flags |
|
38 * |
|
39 * @var array |
|
40 */ |
|
41 public $option_flags = array('nocache'); |
|
42 |
|
43 /** |
|
44 * This function checks if the attributes passed are valid |
|
45 * The attributes passed for the tag to compile are checked against the list of required and |
|
46 * optional attributes. Required attributes must be present. Optional attributes are check against |
|
47 * the corresponding list. The keyword '_any' specifies that any attribute will be accepted |
|
48 * as valid |
|
49 * |
|
50 * @param object $compiler compiler object |
|
51 * @param array $attributes attributes applied to the tag |
|
52 * |
|
53 * @return array of mapped attributes for further processing |
|
54 */ |
|
55 public function getAttributes($compiler, $attributes) { |
|
56 $_indexed_attr = array(); |
|
57 // loop over attributes |
|
58 foreach ($attributes as $key => $mixed) { |
|
59 // shorthand ? |
|
60 if (!is_array($mixed)) { |
|
61 // option flag ? |
|
62 if (in_array(trim($mixed, '\'"'), $this->option_flags)) { |
|
63 $_indexed_attr[trim($mixed, '\'"')] = true; |
|
64 // shorthand attribute ? |
|
65 } elseif (isset($this->shorttag_order[$key])) { |
|
66 $_indexed_attr[$this->shorttag_order[$key]] = $mixed; |
|
67 } else { |
|
68 // too many shorthands |
|
69 $compiler->trigger_template_error('too many shorthand attributes', $compiler->lex->taglineno); |
|
70 } |
|
71 // named attribute |
|
72 } else { |
|
73 $kv = each($mixed); |
|
74 // option flag? |
|
75 if (in_array($kv['key'], $this->option_flags)) { |
|
76 if (is_bool($kv['value'])) { |
|
77 $_indexed_attr[$kv['key']] = $kv['value']; |
|
78 } elseif (is_string($kv['value']) && in_array(trim($kv['value'], '\'"'), array('true', 'false'))) { |
|
79 if (trim($kv['value']) == 'true') { |
|
80 $_indexed_attr[$kv['key']] = true; |
|
81 } else { |
|
82 $_indexed_attr[$kv['key']] = false; |
|
83 } |
|
84 } elseif (is_numeric($kv['value']) && in_array($kv['value'], array(0, 1))) { |
|
85 if ($kv['value'] == 1) { |
|
86 $_indexed_attr[$kv['key']] = true; |
|
87 } else { |
|
88 $_indexed_attr[$kv['key']] = false; |
|
89 } |
|
90 } else { |
|
91 $compiler->trigger_template_error("illegal value of option flag \"{$kv['key']}\"", $compiler->lex->taglineno); |
|
92 } |
|
93 // must be named attribute |
|
94 } else { |
|
95 reset($mixed); |
|
96 $_indexed_attr[key($mixed)] = $mixed[key($mixed)]; |
|
97 } |
|
98 } |
|
99 } |
|
100 // check if all required attributes present |
|
101 foreach ($this->required_attributes as $attr) { |
|
102 if (!array_key_exists($attr, $_indexed_attr)) { |
|
103 $compiler->trigger_template_error("missing \"" . $attr . "\" attribute", $compiler->lex->taglineno); |
|
104 } |
|
105 } |
|
106 // check for not allowed attributes |
|
107 if ($this->optional_attributes != array('_any')) { |
|
108 $tmp_array = array_merge($this->required_attributes, $this->optional_attributes, $this->option_flags); |
|
109 foreach ($_indexed_attr as $key => $dummy) { |
|
110 if (!in_array($key, $tmp_array) && $key !== 0) { |
|
111 $compiler->trigger_template_error("unexpected \"" . $key . "\" attribute", $compiler->lex->taglineno); |
|
112 } |
|
113 } |
|
114 } |
|
115 // default 'false' for all option flags not set |
|
116 foreach ($this->option_flags as $flag) { |
|
117 if (!isset($_indexed_attr[$flag])) { |
|
118 $_indexed_attr[$flag] = false; |
|
119 } |
|
120 } |
|
121 |
|
122 return $_indexed_attr; |
|
123 } |
|
124 |
|
125 /** |
|
126 * Push opening tag name on stack |
|
127 * Optionally additional data can be saved on stack |
|
128 * |
|
129 * @param object $compiler compiler object |
|
130 * @param string $openTag the opening tag's name |
|
131 * @param mixed $data optional data saved |
|
132 */ |
|
133 public function openTag($compiler, $openTag, $data = null) { |
|
134 array_push($compiler->_tag_stack, array($openTag, $data)); |
|
135 } |
|
136 |
|
137 /** |
|
138 * Pop closing tag |
|
139 * Raise an error if this stack-top doesn't match with expected opening tags |
|
140 * |
|
141 * @param object $compiler compiler object |
|
142 * @param array|string $expectedTag the expected opening tag names |
|
143 * |
|
144 * @return mixed any type the opening tag's name or saved data |
|
145 */ |
|
146 public function closeTag($compiler, $expectedTag) { |
|
147 if (count($compiler->_tag_stack) > 0) { |
|
148 // get stacked info |
|
149 list($_openTag, $_data) = array_pop($compiler->_tag_stack); |
|
150 // open tag must match with the expected ones |
|
151 if (in_array($_openTag, (array)$expectedTag)) { |
|
152 if (is_null($_data)) { |
|
153 // return opening tag |
|
154 return $_openTag; |
|
155 } else { |
|
156 // return restored data |
|
157 return $_data; |
|
158 } |
|
159 } |
|
160 // wrong nesting of tags |
|
161 $compiler->trigger_template_error("unclosed {$compiler->smarty->left_delimiter}" . $_openTag . "{$compiler->smarty->right_delimiter} tag"); |
|
162 |
|
163 return; |
|
164 } |
|
165 // wrong nesting of tags |
|
166 $compiler->trigger_template_error("unexpected closing tag", $compiler->lex->taglineno); |
|
167 |
|
168 return; |
|
169 } |
|
170 } |