1<?php 2/** 3 * Smarty Internal Plugin Compile Section 4 * Compiles the {section} {sectionelse} {/section} tags 5 * 6 * @package Smarty 7 * @subpackage Compiler 8 * @author Uwe Tews 9 */ 10 11/** 12 * Smarty Internal Plugin Compile Section Class 13 * 14 * @package Smarty 15 * @subpackage Compiler 16 */ 17class Smarty_Internal_Compile_Section 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('name', 'loop'); 26 27 /** 28 * Attribute definition: Overwrites base class. 29 * 30 * @var array 31 * @see Smarty_Internal_CompileBase 32 */ 33 public $shorttag_order = array('name', 'loop'); 34 35 /** 36 * Attribute definition: Overwrites base class. 37 * 38 * @var array 39 * @see Smarty_Internal_CompileBase 40 */ 41 public $optional_attributes = array('start', 'step', 'max', 'show', 'properties'); 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 = 'section'; 56 57 /** 58 * Valid properties of $smarty.section.name.xxx variable 59 * 60 * @var array 61 */ 62 public $nameProperties = array( 63 'first', 'last', 'index', 'iteration', 'show', 'total', 'rownum', 'index_prev', 64 'index_next', 'loop' 65 ); 66 67 /** 68 * {section} tag has no item properties 69 * 70 * @var array 71 */ 72 public $itemProperties = null; 73 74 /** 75 * {section} tag has always name attribute 76 * 77 * @var bool 78 */ 79 public $isNamed = true; 80 81 /** 82 * Compiles code for the {section} tag 83 * 84 * @param array $args array with attributes from parser 85 * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object 86 * 87 * @return string compiled code 88 * @throws \SmartyCompilerException 89 * @throws \SmartyException 90 */ 91 public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) 92 { 93 $compiler->loopNesting++; 94 // check and get attributes 95 $_attr = $this->getAttributes($compiler, $args); 96 $attributes = array('name' => $compiler->getId($_attr[ 'name' ])); 97 unset($_attr[ 'name' ]); 98 foreach ($attributes as $a => $v) { 99 if ($v === false) { 100 $compiler->trigger_template_error("'{$a}' attribute/variable has illegal value", null, true); 101 } 102 } 103 $local = "\$__section_{$attributes['name']}_" . $this->counter++ . '_'; 104 $sectionVar = "\$_smarty_tpl->tpl_vars['__smarty_section_{$attributes['name']}']"; 105 $this->openTag($compiler, 'section', array('section', $compiler->nocache, $local, $sectionVar)); 106 // maybe nocache because of nocache variables 107 $compiler->nocache = $compiler->nocache | $compiler->tag_nocache; 108 $initLocal = array(); 109 $initNamedProperty = array(); 110 $initFor = array(); 111 $incFor = array(); 112 $cmpFor = array(); 113 $propValue = array( 114 'index' => "{$sectionVar}->value['index']", 'show' => 'true', 'step' => 1, 115 'iteration' => "{$local}iteration", 116 ); 117 $propType = array('index' => 2, 'iteration' => 2, 'show' => 0, 'step' => 0,); 118 // search for used tag attributes 119 $this->scanForProperties($attributes, $compiler); 120 if (!empty($this->matchResults[ 'named' ])) { 121 $namedAttr = $this->matchResults[ 'named' ]; 122 } 123 if (isset($_attr[ 'properties' ]) && preg_match_all("/['](.*?)[']/", $_attr[ 'properties' ], $match)) { 124 foreach ($match[ 1 ] as $prop) { 125 if (in_array($prop, $this->nameProperties)) { 126 $namedAttr[ $prop ] = true; 127 } else { 128 $compiler->trigger_template_error("Invalid property '{$prop}'", null, true); 129 } 130 } 131 } 132 $namedAttr[ 'index' ] = true; 133 $output = "<?php\n"; 134 foreach ($_attr as $attr_name => $attr_value) { 135 switch ($attr_name) { 136 case 'loop': 137 if (is_numeric($attr_value)) { 138 $v = (int)$attr_value; 139 $t = 0; 140 } else { 141 $v = "(is_array(@\$_loop=$attr_value) ? count(\$_loop) : max(0, (int) \$_loop))"; 142 $t = 1; 143 } 144 if ($t === 1) { 145 $initLocal[ 'loop' ] = $v; 146 $v = "{$local}loop"; 147 } 148 break; 149 case 'show': 150 if (is_bool($attr_value)) { 151 $v = $attr_value ? 'true' : 'false'; 152 $t = 0; 153 } else { 154 $v = "(bool) $attr_value"; 155 $t = 3; 156 } 157 break; 158 case 'step': 159 if (is_numeric($attr_value)) { 160 $v = (int)$attr_value; 161 $v = ($v === 0) ? 1 : $v; 162 $t = 0; 163 break; 164 } 165 $initLocal[ 'step' ] = "((int)@$attr_value) === 0 ? 1 : (int)@$attr_value"; 166 $v = "{$local}step"; 167 $t = 2; 168 break; 169 case 'max': 170 case 'start': 171 if (is_numeric($attr_value)) { 172 $v = (int)$attr_value; 173 $t = 0; 174 break; 175 } 176 $v = "(int)@$attr_value"; 177 $t = 3; 178 break; 179 } 180 if ($t === 3 && $compiler->getId($attr_value)) { 181 $t = 1; 182 } 183 $propValue[ $attr_name ] = $v; 184 $propType[ $attr_name ] = $t; 185 } 186 if (isset($namedAttr[ 'step' ])) { 187 $initNamedProperty[ 'step' ] = $propValue[ 'step' ]; 188 } 189 if (isset($namedAttr[ 'iteration' ])) { 190 $propValue[ 'iteration' ] = "{$sectionVar}->value['iteration']"; 191 } 192 $incFor[ 'iteration' ] = "{$propValue['iteration']}++"; 193 $initFor[ 'iteration' ] = "{$propValue['iteration']} = 1"; 194 if ($propType[ 'step' ] === 0) { 195 if ($propValue[ 'step' ] === 1) { 196 $incFor[ 'index' ] = "{$sectionVar}->value['index']++"; 197 } elseif ($propValue[ 'step' ] > 1) { 198 $incFor[ 'index' ] = "{$sectionVar}->value['index'] += {$propValue['step']}"; 199 } else { 200 $incFor[ 'index' ] = "{$sectionVar}->value['index'] -= " . -$propValue[ 'step' ]; 201 } 202 } else { 203 $incFor[ 'index' ] = "{$sectionVar}->value['index'] += {$propValue['step']}"; 204 } 205 if (!isset($propValue[ 'max' ])) { 206 $propValue[ 'max' ] = $propValue[ 'loop' ]; 207 $propType[ 'max' ] = $propType[ 'loop' ]; 208 } elseif ($propType[ 'max' ] !== 0) { 209 $propValue[ 'max' ] = "{$propValue['max']} < 0 ? {$propValue['loop']} : {$propValue['max']}"; 210 $propType[ 'max' ] = 1; 211 } else { 212 if ($propValue[ 'max' ] < 0) { 213 $propValue[ 'max' ] = $propValue[ 'loop' ]; 214 $propType[ 'max' ] = $propType[ 'loop' ]; 215 } 216 } 217 if (!isset($propValue[ 'start' ])) { 218 $start_code = 219 array(1 => "{$propValue['step']} > 0 ? ", 2 => '0', 3 => ' : ', 4 => $propValue[ 'loop' ], 5 => ' - 1'); 220 if ($propType[ 'loop' ] === 0) { 221 $start_code[ 5 ] = ''; 222 $start_code[ 4 ] = $propValue[ 'loop' ] - 1; 223 } 224 if ($propType[ 'step' ] === 0) { 225 if ($propValue[ 'step' ] > 0) { 226 $start_code = array(1 => '0'); 227 $propType[ 'start' ] = 0; 228 } else { 229 $start_code[ 1 ] = $start_code[ 2 ] = $start_code[ 3 ] = ''; 230 $propType[ 'start' ] = $propType[ 'loop' ]; 231 } 232 } else { 233 $propType[ 'start' ] = 1; 234 } 235 $propValue[ 'start' ] = join('', $start_code); 236 } else { 237 $start_code = 238 array( 239 1 => "{$propValue['start']} < 0 ? ", 2 => 'max(', 3 => "{$propValue['step']} > 0 ? ", 4 => '0', 240 5 => ' : ', 6 => '-1', 7 => ', ', 8 => "{$propValue['start']} + {$propValue['loop']}", 10 => ')', 241 11 => ' : ', 12 => 'min(', 13 => $propValue[ 'start' ], 14 => ', ', 242 15 => "{$propValue['step']} > 0 ? ", 16 => $propValue[ 'loop' ], 17 => ' : ', 243 18 => $propType[ 'loop' ] === 0 ? $propValue[ 'loop' ] - 1 : "{$propValue['loop']} - 1", 244 19 => ')' 245 ); 246 if ($propType[ 'step' ] === 0) { 247 $start_code[ 3 ] = $start_code[ 5 ] = $start_code[ 15 ] = $start_code[ 17 ] = ''; 248 if ($propValue[ 'step' ] > 0) { 249 $start_code[ 6 ] = $start_code[ 18 ] = ''; 250 } else { 251 $start_code[ 4 ] = $start_code[ 16 ] = ''; 252 } 253 } 254 if ($propType[ 'start' ] === 0) { 255 if ($propType[ 'loop' ] === 0) { 256 $start_code[ 8 ] = $propValue[ 'start' ] + $propValue[ 'loop' ]; 257 } 258 $propType[ 'start' ] = $propType[ 'step' ] + $propType[ 'loop' ]; 259 $start_code[ 1 ] = ''; 260 if ($propValue[ 'start' ] < 0) { 261 for ($i = 11; $i <= 19; $i++) { 262 $start_code[ $i ] = ''; 263 } 264 if ($propType[ 'start' ] === 0) { 265 $start_code = array( 266 max( 267 $propValue[ 'step' ] > 0 ? 0 : -1, 268 $propValue[ 'start' ] + $propValue[ 'loop' ] 269 ) 270 ); 271 } 272 } else { 273 for ($i = 1; $i <= 11; $i++) { 274 $start_code[ $i ] = ''; 275 } 276 if ($propType[ 'start' ] === 0) { 277 $start_code = 278 array( 279 min( 280 $propValue[ 'step' ] > 0 ? $propValue[ 'loop' ] : $propValue[ 'loop' ] - 1, 281 $propValue[ 'start' ] 282 ) 283 ); 284 } 285 } 286 } 287 $propValue[ 'start' ] = join('', $start_code); 288 } 289 if ($propType[ 'start' ] !== 0) { 290 $initLocal[ 'start' ] = $propValue[ 'start' ]; 291 $propValue[ 'start' ] = "{$local}start"; 292 } 293 $initFor[ 'index' ] = "{$sectionVar}->value['index'] = {$propValue['start']}"; 294 if (!isset($_attr[ 'start' ]) && !isset($_attr[ 'step' ]) && !isset($_attr[ 'max' ])) { 295 $propValue[ 'total' ] = $propValue[ 'loop' ]; 296 $propType[ 'total' ] = $propType[ 'loop' ]; 297 } else { 298 $propType[ 'total' ] = 299 $propType[ 'start' ] + $propType[ 'loop' ] + $propType[ 'step' ] + $propType[ 'max' ]; 300 if ($propType[ 'total' ] === 0) { 301 $propValue[ 'total' ] = 302 min( 303 ceil( 304 ($propValue[ 'step' ] > 0 ? $propValue[ 'loop' ] - $propValue[ 'start' ] : 305 (int)$propValue[ 'start' ] + 1) / abs($propValue[ 'step' ]) 306 ), 307 $propValue[ 'max' ] 308 ); 309 } else { 310 $total_code = array( 311 1 => 'min(', 2 => 'ceil(', 3 => '(', 4 => "{$propValue['step']} > 0 ? ", 312 5 => $propValue[ 'loop' ], 6 => ' - ', 7 => $propValue[ 'start' ], 8 => ' : ', 313 9 => $propValue[ 'start' ], 10 => '+ 1', 11 => ')', 12 => '/ ', 13 => 'abs(', 314 14 => $propValue[ 'step' ], 15 => ')', 16 => ')', 17 => ", {$propValue['max']})", 315 ); 316 if (!isset($propValue[ 'max' ])) { 317 $total_code[ 1 ] = $total_code[ 17 ] = ''; 318 } 319 if ($propType[ 'loop' ] + $propType[ 'start' ] === 0) { 320 $total_code[ 5 ] = $propValue[ 'loop' ] - $propValue[ 'start' ]; 321 $total_code[ 6 ] = $total_code[ 7 ] = ''; 322 } 323 if ($propType[ 'start' ] === 0) { 324 $total_code[ 9 ] = (int)$propValue[ 'start' ] + 1; 325 $total_code[ 10 ] = ''; 326 } 327 if ($propType[ 'step' ] === 0) { 328 $total_code[ 13 ] = $total_code[ 15 ] = ''; 329 if ($propValue[ 'step' ] === 1 || $propValue[ 'step' ] === -1) { 330 $total_code[ 2 ] = $total_code[ 12 ] = $total_code[ 14 ] = $total_code[ 16 ] = ''; 331 } elseif ($propValue[ 'step' ] < 0) { 332 $total_code[ 14 ] = -$propValue[ 'step' ]; 333 } 334 $total_code[ 4 ] = ''; 335 if ($propValue[ 'step' ] > 0) { 336 $total_code[ 8 ] = $total_code[ 9 ] = $total_code[ 10 ] = ''; 337 } else { 338 $total_code[ 5 ] = $total_code[ 6 ] = $total_code[ 7 ] = $total_code[ 8 ] = ''; 339 } 340 } 341 $propValue[ 'total' ] = join('', $total_code); 342 } 343 } 344 if (isset($namedAttr[ 'loop' ])) { 345 $initNamedProperty[ 'loop' ] = "'loop' => {$propValue['loop']}"; 346 } 347 if (isset($namedAttr[ 'total' ])) { 348 $initNamedProperty[ 'total' ] = "'total' => {$propValue['total']}"; 349 if ($propType[ 'total' ] > 0) { 350 $propValue[ 'total' ] = "{$sectionVar}->value['total']"; 351 } 352 } elseif ($propType[ 'total' ] > 0) { 353 $initLocal[ 'total' ] = $propValue[ 'total' ]; 354 $propValue[ 'total' ] = "{$local}total"; 355 } 356 $cmpFor[ 'iteration' ] = "{$propValue['iteration']} <= {$propValue['total']}"; 357 foreach ($initLocal as $key => $code) { 358 $output .= "{$local}{$key} = {$code};\n"; 359 } 360 $_vars = 'array(' . join(', ', $initNamedProperty) . ')'; 361 $output .= "{$sectionVar} = new Smarty_Variable({$_vars});\n"; 362 $cond_code = "{$propValue['total']} !== 0"; 363 if ($propType[ 'total' ] === 0) { 364 if ($propValue[ 'total' ] === 0) { 365 $cond_code = 'false'; 366 } else { 367 $cond_code = 'true'; 368 } 369 } 370 if ($propType[ 'show' ] > 0) { 371 $output .= "{$local}show = {$propValue['show']} ? {$cond_code} : false;\n"; 372 $output .= "if ({$local}show) {\n"; 373 } elseif ($propValue[ 'show' ] === 'true') { 374 $output .= "if ({$cond_code}) {\n"; 375 } else { 376 $output .= "if (false) {\n"; 377 } 378 $jinit = join(', ', $initFor); 379 $jcmp = join(', ', $cmpFor); 380 $jinc = join(', ', $incFor); 381 $output .= "for ({$jinit}; {$jcmp}; {$jinc}){\n"; 382 if (isset($namedAttr[ 'rownum' ])) { 383 $output .= "{$sectionVar}->value['rownum'] = {$propValue['iteration']};\n"; 384 } 385 if (isset($namedAttr[ 'index_prev' ])) { 386 $output .= "{$sectionVar}->value['index_prev'] = {$propValue['index']} - {$propValue['step']};\n"; 387 } 388 if (isset($namedAttr[ 'index_next' ])) { 389 $output .= "{$sectionVar}->value['index_next'] = {$propValue['index']} + {$propValue['step']};\n"; 390 } 391 if (isset($namedAttr[ 'first' ])) { 392 $output .= "{$sectionVar}->value['first'] = ({$propValue['iteration']} === 1);\n"; 393 } 394 if (isset($namedAttr[ 'last' ])) { 395 $output .= "{$sectionVar}->value['last'] = ({$propValue['iteration']} === {$propValue['total']});\n"; 396 } 397 $output .= '?>'; 398 return $output; 399 } 400} 401 402/** 403 * Smarty Internal Plugin Compile Sectionelse Class 404 * 405 * @package Smarty 406 * @subpackage Compiler 407 */ 408class Smarty_Internal_Compile_Sectionelse extends Smarty_Internal_CompileBase 409{ 410 /** 411 * Compiles code for the {sectionelse} tag 412 * 413 * @param array $args array with attributes from parser 414 * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object 415 * 416 * @return string compiled code 417 */ 418 public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) 419 { 420 // check and get attributes 421 $_attr = $this->getAttributes($compiler, $args); 422 list($openTag, $nocache, $local, $sectionVar) = $this->closeTag($compiler, array('section')); 423 $this->openTag($compiler, 'sectionelse', array('sectionelse', $nocache, $local, $sectionVar)); 424 return "<?php }} else {\n ?>"; 425 } 426} 427 428/** 429 * Smarty Internal Plugin Compile Sectionclose Class 430 * 431 * @package Smarty 432 * @subpackage Compiler 433 */ 434class Smarty_Internal_Compile_Sectionclose extends Smarty_Internal_CompileBase 435{ 436 /** 437 * Compiles code for the {/section} tag 438 * 439 * @param array $args array with attributes from parser 440 * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object 441 * 442 * @return string compiled code 443 */ 444 public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler) 445 { 446 $compiler->loopNesting--; 447 // must endblock be nocache? 448 if ($compiler->nocache) { 449 $compiler->tag_nocache = true; 450 } 451 list($openTag, $compiler->nocache, $local, $sectionVar) = 452 $this->closeTag($compiler, array('section', 'sectionelse')); 453 $output = "<?php\n"; 454 if ($openTag === 'sectionelse') { 455 $output .= "}\n"; 456 } else { 457 $output .= "}\n}\n"; 458 } 459 $output .= '?>'; 460 return $output; 461 } 462} 463