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 */ 17class 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 .= "if (\$_from !== null) {\n"; 223 $output .= "foreach (\$_from as {$keyTerm}{$itemVar}->value) {\n"; 224 if (isset($attributes[ 'key' ]) && isset($itemAttr[ 'key' ])) { 225 $output .= "\$_smarty_tpl->tpl_vars['{$key}']->value = {$itemVar}->key;\n"; 226 } 227 if (isset($itemAttr[ 'iteration' ])) { 228 $output .= "{$itemVar}->iteration++;\n"; 229 } 230 if (isset($itemAttr[ 'index' ])) { 231 $output .= "{$itemVar}->index++;\n"; 232 } 233 if (isset($itemAttr[ 'first' ])) { 234 $output .= "{$itemVar}->first = !{$itemVar}->index;\n"; 235 } 236 if (isset($itemAttr[ 'last' ])) { 237 $output .= "{$itemVar}->last = {$itemVar}->iteration === {$itemVar}->total;\n"; 238 } 239 if (isset($foreachVar)) { 240 if (isset($namedAttr[ 'iteration' ])) { 241 $output .= "{$foreachVar}->value['iteration']++;\n"; 242 } 243 if (isset($namedAttr[ 'index' ])) { 244 $output .= "{$foreachVar}->value['index']++;\n"; 245 } 246 if (isset($namedAttr[ 'first' ])) { 247 $output .= "{$foreachVar}->value['first'] = !{$foreachVar}->value['index'];\n"; 248 } 249 if (isset($namedAttr[ 'last' ])) { 250 $output .= "{$foreachVar}->value['last'] = {$foreachVar}->value['iteration'] === {$foreachVar}->value['total'];\n"; 251 } 252 } 253 if (!empty($itemAttr)) { 254 $output .= "{$local}saved = {$itemVar};\n"; 255 } 256 $output .= '?>'; 257 return $output; 258 } 259 260 /** 261 * Compiles code for to restore saved template variables 262 * 263 * @param int $levels number of levels to restore 264 * 265 * @return string compiled code 266 */ 267 public function compileRestore($levels) 268 { 269 return "\$_smarty_tpl->smarty->ext->_foreach->restore(\$_smarty_tpl, {$levels});"; 270 } 271} 272 273/** 274 * Smarty Internal Plugin Compile Foreachelse Class 275 * 276 * @package Smarty 277 * @subpackage Compiler 278 */ 279class Smarty_Internal_Compile_Foreachelse extends Smarty_Internal_CompileBase 280{ 281 /** 282 * Compiles code for the {foreachelse} tag 283 * 284 * @param array $args array with attributes from parser 285 * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object 286 * 287 * @return string compiled code 288 */ 289 public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) 290 { 291 // check and get attributes 292 $_attr = $this->getAttributes($compiler, $args); 293 list($openTag, $nocache, $local, $itemVar, $restore) = $this->closeTag($compiler, array('foreach')); 294 $this->openTag($compiler, 'foreachelse', array('foreachelse', $nocache, $local, $itemVar, 0)); 295 $output = "<?php\n"; 296 if ($restore === 2) { 297 $output .= "{$itemVar} = {$local}saved;\n"; 298 } 299 $output .= "}\n} else {\n?>"; 300 return $output; 301 } 302} 303 304/** 305 * Smarty Internal Plugin Compile Foreachclose Class 306 * 307 * @package Smarty 308 * @subpackage Compiler 309 */ 310class Smarty_Internal_Compile_Foreachclose extends Smarty_Internal_CompileBase 311{ 312 /** 313 * Compiles code for the {/foreach} tag 314 * 315 * @param array $args array with attributes from parser 316 * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object 317 * 318 * @return string compiled code 319 * @throws \SmartyCompilerException 320 */ 321 public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) 322 { 323 $compiler->loopNesting--; 324 // must endblock be nocache? 325 if ($compiler->nocache) { 326 $compiler->tag_nocache = true; 327 } 328 list( 329 $openTag, $compiler->nocache, $local, $itemVar, $restore 330 ) = $this->closeTag($compiler, array('foreach', 'foreachelse')); 331 $output = "<?php\n"; 332 if ($restore === 2) { 333 $output .= "{$itemVar} = {$local}saved;\n"; 334 } 335 if ($restore > 0) { 336 $output .= "}\n"; 337 } 338 $output .= "}\n"; 339 /* @var Smarty_Internal_Compile_Foreach $foreachCompiler */ 340 $foreachCompiler = $compiler->getTagCompiler('foreach'); 341 $output .= $foreachCompiler->compileRestore(1); 342 $output .= "?>"; 343 return $output; 344 } 345} 346