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