1: <?php
2: /**
3: * Smarty Internal Plugin Compile Foreach
4: * Compiles the {foreach} {foreachelse} {/foreach} tags
5: *
6: * @package Smarty
7: * @subpackage Compiler
8: * @author Uwe Tews
9: */
10:
11: /**
12: * Smarty Internal Plugin Compile Foreach Class
13: *
14: * @package Smarty
15: * @subpackage Compiler
16: */
17: class Smarty_Internal_Compile_Foreach extends Smarty_Internal_Compile_Private_ForeachSection
18: {
19: /**
20: * Attribute definition: Overwrites base class.
21: *
22: * @var array
23: * @see Smarty_Internal_CompileBase
24: */
25: public $required_attributes = array('from', 'item');
26:
27: /**
28: * Attribute definition: Overwrites base class.
29: *
30: * @var array
31: * @see Smarty_Internal_CompileBase
32: */
33: public $optional_attributes = array('name', 'key', 'properties');
34:
35: /**
36: * Attribute definition: Overwrites base class.
37: *
38: * @var array
39: * @see Smarty_Internal_CompileBase
40: */
41: public $shorttag_order = array('from', 'item', 'key', 'name');
42:
43: /**
44: * counter
45: *
46: * @var int
47: */
48: public $counter = 0;
49:
50: /**
51: * Name of this tag
52: *
53: * @var string
54: */
55: public $tagName = 'foreach';
56:
57: /**
58: * Valid properties of $smarty.foreach.name.xxx variable
59: *
60: * @var array
61: */
62: public $nameProperties = array('first', 'last', 'index', 'iteration', 'show', 'total');
63:
64: /**
65: * Valid properties of $item@xxx variable
66: *
67: * @var array
68: */
69: public $itemProperties = array('first', 'last', 'index', 'iteration', 'show', 'total', 'key');
70:
71: /**
72: * Flag if tag had name attribute
73: *
74: * @var bool
75: */
76: public $isNamed = false;
77:
78: /**
79: * Compiles code for the {foreach} tag
80: *
81: * @param array $args array with attributes from parser
82: * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
83: *
84: * @return string compiled code
85: * @throws \SmartyCompilerException
86: * @throws \SmartyException
87: */
88: public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
89: {
90: $compiler->loopNesting++;
91: // init
92: $this->isNamed = false;
93: // check and get attributes
94: $_attr = $this->getAttributes($compiler, $args);
95: $from = $_attr[ 'from' ];
96: $item = $compiler->getId($_attr[ 'item' ]);
97: if ($item === false) {
98: $item = $compiler->getVariableName($_attr[ 'item' ]);
99: }
100: $key = $name = null;
101: $attributes = array('item' => $item);
102: if (isset($_attr[ 'key' ])) {
103: $key = $compiler->getId($_attr[ 'key' ]);
104: if ($key === false) {
105: $key = $compiler->getVariableName($_attr[ 'key' ]);
106: }
107: $attributes[ 'key' ] = $key;
108: }
109: if (isset($_attr[ 'name' ])) {
110: $this->isNamed = true;
111: $name = $attributes[ 'name' ] = $compiler->getId($_attr[ 'name' ]);
112: }
113: foreach ($attributes as $a => $v) {
114: if ($v === false) {
115: $compiler->trigger_template_error("'{$a}' attribute/variable has illegal value", null, true);
116: }
117: }
118: $fromName = $compiler->getVariableName($_attr[ 'from' ]);
119: if ($fromName) {
120: foreach (array('item', 'key') as $a) {
121: if (isset($attributes[ $a ]) && $attributes[ $a ] === $fromName) {
122: $compiler->trigger_template_error(
123: "'{$a}' and 'from' may not have same variable name '{$fromName}'",
124: null,
125: true
126: );
127: }
128: }
129: }
130: $itemVar = "\$_smarty_tpl->tpl_vars['{$item}']";
131: $local = '$__foreach_' . $attributes[ 'item' ] . '_' . $this->counter++ . '_';
132: // search for used tag attributes
133: $itemAttr = array();
134: $namedAttr = array();
135: $this->scanForProperties($attributes, $compiler);
136: if (!empty($this->matchResults[ 'item' ])) {
137: $itemAttr = $this->matchResults[ 'item' ];
138: }
139: if (!empty($this->matchResults[ 'named' ])) {
140: $namedAttr = $this->matchResults[ 'named' ];
141: }
142: if (isset($_attr[ 'properties' ]) && preg_match_all('/[\'](.*?)[\']/', $_attr[ 'properties' ], $match)) {
143: foreach ($match[ 1 ] as $prop) {
144: if (in_array($prop, $this->itemProperties)) {
145: $itemAttr[ $prop ] = true;
146: } else {
147: $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
148: }
149: }
150: if ($this->isNamed) {
151: foreach ($match[ 1 ] as $prop) {
152: if (in_array($prop, $this->nameProperties)) {
153: $nameAttr[ $prop ] = true;
154: } else {
155: $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
156: }
157: }
158: }
159: }
160: if (isset($itemAttr[ 'first' ])) {
161: $itemAttr[ 'index' ] = true;
162: }
163: if (isset($namedAttr[ 'first' ])) {
164: $namedAttr[ 'index' ] = true;
165: }
166: if (isset($namedAttr[ 'last' ])) {
167: $namedAttr[ 'iteration' ] = true;
168: $namedAttr[ 'total' ] = true;
169: }
170: if (isset($itemAttr[ 'last' ])) {
171: $itemAttr[ 'iteration' ] = true;
172: $itemAttr[ 'total' ] = true;
173: }
174: if (isset($namedAttr[ 'show' ])) {
175: $namedAttr[ 'total' ] = true;
176: }
177: if (isset($itemAttr[ 'show' ])) {
178: $itemAttr[ 'total' ] = true;
179: }
180: $keyTerm = '';
181: if (isset($attributes[ 'key' ])) {
182: $keyTerm = "\$_smarty_tpl->tpl_vars['{$key}']->value => ";
183: }
184: if (isset($itemAttr[ 'key' ])) {
185: $keyTerm = "{$itemVar}->key => ";
186: }
187: if ($this->isNamed) {
188: $foreachVar = "\$_smarty_tpl->tpl_vars['__smarty_foreach_{$attributes['name']}']";
189: }
190: $needTotal = isset($itemAttr[ 'total' ]);
191: // Register tag
192: $this->openTag(
193: $compiler,
194: 'foreach',
195: array('foreach', $compiler->nocache, $local, $itemVar, empty($itemAttr) ? 1 : 2)
196: );
197: // maybe nocache because of nocache variables
198: $compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
199: // generate output code
200: $output = "<?php\n";
201: $output .= "\$_from = \$_smarty_tpl->smarty->ext->_foreach->init(\$_smarty_tpl, $from, " .
202: var_export($item, true);
203: if ($name || $needTotal || $key) {
204: $output .= ', ' . var_export($needTotal, true);
205: }
206: if ($name || $key) {
207: $output .= ', ' . var_export($key, true);
208: }
209: if ($name) {
210: $output .= ', ' . var_export($name, true) . ', ' . var_export($namedAttr, true);
211: }
212: $output .= ");\n";
213: if (isset($itemAttr[ 'show' ])) {
214: $output .= "{$itemVar}->show = ({$itemVar}->total > 0);\n";
215: }
216: if (isset($itemAttr[ 'iteration' ])) {
217: $output .= "{$itemVar}->iteration = 0;\n";
218: }
219: if (isset($itemAttr[ 'index' ])) {
220: $output .= "{$itemVar}->index = -1;\n";
221: }
222: $output .= "{$itemVar}->do_else = true;\n";
223: $output .= "if (\$_from !== null) foreach (\$_from as {$keyTerm}{$itemVar}->value) {\n";
224: $output .= "{$itemVar}->do_else = false;\n";
225: if (isset($attributes[ 'key' ]) && isset($itemAttr[ 'key' ])) {
226: $output .= "\$_smarty_tpl->tpl_vars['{$key}']->value = {$itemVar}->key;\n";
227: }
228: if (isset($itemAttr[ 'iteration' ])) {
229: $output .= "{$itemVar}->iteration++;\n";
230: }
231: if (isset($itemAttr[ 'index' ])) {
232: $output .= "{$itemVar}->index++;\n";
233: }
234: if (isset($itemAttr[ 'first' ])) {
235: $output .= "{$itemVar}->first = !{$itemVar}->index;\n";
236: }
237: if (isset($itemAttr[ 'last' ])) {
238: $output .= "{$itemVar}->last = {$itemVar}->iteration === {$itemVar}->total;\n";
239: }
240: if (isset($foreachVar)) {
241: if (isset($namedAttr[ 'iteration' ])) {
242: $output .= "{$foreachVar}->value['iteration']++;\n";
243: }
244: if (isset($namedAttr[ 'index' ])) {
245: $output .= "{$foreachVar}->value['index']++;\n";
246: }
247: if (isset($namedAttr[ 'first' ])) {
248: $output .= "{$foreachVar}->value['first'] = !{$foreachVar}->value['index'];\n";
249: }
250: if (isset($namedAttr[ 'last' ])) {
251: $output .= "{$foreachVar}->value['last'] = {$foreachVar}->value['iteration'] === {$foreachVar}->value['total'];\n";
252: }
253: }
254: if (!empty($itemAttr)) {
255: $output .= "{$local}saved = {$itemVar};\n";
256: }
257: $output .= '?>';
258: return $output;
259: }
260:
261: /**
262: * Compiles code for to restore saved template variables
263: *
264: * @param int $levels number of levels to restore
265: *
266: * @return string compiled code
267: */
268: public function compileRestore($levels)
269: {
270: return "\$_smarty_tpl->smarty->ext->_foreach->restore(\$_smarty_tpl, {$levels});";
271: }
272: }
273:
274: /**
275: * Smarty Internal Plugin Compile Foreachelse Class
276: *
277: * @package Smarty
278: * @subpackage Compiler
279: */
280: class Smarty_Internal_Compile_Foreachelse extends Smarty_Internal_CompileBase
281: {
282: /**
283: * Compiles code for the {foreachelse} tag
284: *
285: * @param array $args array with attributes from parser
286: * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
287: *
288: * @return string compiled code
289: */
290: public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
291: {
292: // check and get attributes
293: $_attr = $this->getAttributes($compiler, $args);
294: list($openTag, $nocache, $local, $itemVar, $restore) = $this->closeTag($compiler, array('foreach'));
295: $this->openTag($compiler, 'foreachelse', array('foreachelse', $nocache, $local, $itemVar, 0));
296: $output = "<?php\n";
297: if ($restore === 2) {
298: $output .= "{$itemVar} = {$local}saved;\n";
299: }
300: $output .= "}\nif ({$itemVar}->do_else) {\n?>";
301: return $output;
302: }
303: }
304:
305: /**
306: * Smarty Internal Plugin Compile Foreachclose Class
307: *
308: * @package Smarty
309: * @subpackage Compiler
310: */
311: class Smarty_Internal_Compile_Foreachclose extends Smarty_Internal_CompileBase
312: {
313: /**
314: * Compiles code for the {/foreach} tag
315: *
316: * @param array $args array with attributes from parser
317: * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
318: *
319: * @return string compiled code
320: * @throws \SmartyCompilerException
321: */
322: public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
323: {
324: $compiler->loopNesting--;
325: // must endblock be nocache?
326: if ($compiler->nocache) {
327: $compiler->tag_nocache = true;
328: }
329: list(
330: $openTag, $compiler->nocache, $local, $itemVar, $restore
331: ) = $this->closeTag($compiler, array('foreach', 'foreachelse'));
332: $output = "<?php\n";
333: if ($restore === 2) {
334: $output .= "{$itemVar} = {$local}saved;\n";
335: }
336: $output .= "}\n";
337: /* @var Smarty_Internal_Compile_Foreach $foreachCompiler */
338: $foreachCompiler = $compiler->getTagCompiler('foreach');
339: $output .= $foreachCompiler->compileRestore(1);
340: $output .= "?>";
341: return $output;
342: }
343: }
344: